答案 0 :(得分:53)
这不是你要找的吗?
<StackPanel Margin="0,10,0,0" />
第一个值是左边距,然后是顶部,然后是右边,最后一个但不是最少底部。
我不确定你是否想将它绑定到某个东西,但如果没有,那就行了。
答案 1 :(得分:35)
关键是要意识到在代码中设置它是这样的:
sp2.Margin = new System.Windows.Thickness{ Left = 5 };
相当于:
sp2.Margin = new System.Windows.Thickness{ Left = 5, Top = 0, Right = 0, Bottom = 0 };
您无法通过代码或XAML 在Thickness
实例中设置单个值。如果您没有设置某些值,它们将隐式为零。因此,您可以这样做将您在其他问题中接受的代码示例转换为XAML等效项:
<StackPanel Margin="{Binding TopMargin, Converter={StaticResource MyConverter}}"/>
其中MyConverter
只返回Thickness
,只设置Top
并将所有其他值保留为零。
当然,您可以编写自己的控件, 将这些单独的值公开为依赖属性,以使您的代码更清晰:
<CustomBorder TopMargin="{Binding TopMargin}">
</CustomBorder>
比自定义控件更好的选择是编写附加属性并使用依赖项属性设置器中的上述代码更改厚度。以下代码可用于具有保证金的所有控件。
public static readonly DependencyProperty TopMarginProperty =
DependencyProperty.RegisterAttached("TopMargin", typeof(int), typeof(FrameworkElement),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
public static void SetTopMargin(FrameworkElement element, int value)
{
// set top margin in element.Margin
}
public static int GetTopMargin(FrameworkElement element)
{
// get top margin from element.Margin
}
如果您将此与行为结合使用,则可以在TopMargin属性上获得通知更改。
答案 2 :(得分:19)
这属于WPF修正案:
您无法使用附加属性或边距的绑定动态设置元素的位置,而无需在后面写几行代码。
请勿将此技术与其他技术进行比较。
您的问题列在#9。
答案 3 :(得分:2)
您不能仅使用绑定定义上边距,因为Margin
的类型为Thickness
,而不是依赖项对象。但是,您可以使用MultiValueConverter
来获取1个边距值来制作1个厚度对象
转换器:
public class ThicknessMultiConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double left = System.Convert.ToDouble(values[0]);
double top = System.Convert.ToDouble(values[1]);
double right = System.Convert.ToDouble(values[2]);
double bottom = System.Convert.ToDouble(values[3]);
return new Thickness(left, top, right, bottom);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
Thickness thickness = (Thickness)value;
return new object[]
{
thickness.Left,
thickness.Top,
thickness.Right,
thickness.Bottom
};
}
#endregion
}
XAML:
<StackPanel>
<StackPanel.Margin>
<MultiBinding Converter="{StaticResource myThicknessConverter}">
<Binding Path="LeftMargin"/>
<Binding Path="TopMargin"/>
<Binding Path="RightMargin"/>
<Binding Path="BottomMargin"/>
</MultiBinding>
</StackPanel.Margin>
</StackPanel>
答案 4 :(得分:2)
刚刚编写了一些附加属性,可以很容易地从绑定或静态资源设置单个Margin值:
public class Margin
{
public static readonly DependencyProperty LeftProperty = DependencyProperty.RegisterAttached(
"Left",
typeof(double),
typeof(Margin),
new PropertyMetadata(0.0));
public static void SetLeft(UIElement element, double value)
{
var frameworkElement = element as FrameworkElement;
if (frameworkElement != null)
{
Thickness currentMargin = frameworkElement.Margin;
frameworkElement.Margin = new Thickness(value, currentMargin.Top, currentMargin.Right, currentMargin.Bottom);
}
}
public static double GetLeft(UIElement element)
{
return 0;
}
public static readonly DependencyProperty TopProperty = DependencyProperty.RegisterAttached(
"Top",
typeof(double),
typeof(Margin),
new PropertyMetadata(0.0));
public static void SetTop(UIElement element, double value)
{
var frameworkElement = element as FrameworkElement;
if (frameworkElement != null)
{
Thickness currentMargin = frameworkElement.Margin;
frameworkElement.Margin = new Thickness(currentMargin.Left, value, currentMargin.Right, currentMargin.Bottom);
}
}
public static double GetTop(UIElement element)
{
return 0;
}
public static readonly DependencyProperty RightProperty = DependencyProperty.RegisterAttached(
"Right",
typeof(double),
typeof(Margin),
new PropertyMetadata(0.0));
public static void SetRight(UIElement element, double value)
{
var frameworkElement = element as FrameworkElement;
if (frameworkElement != null)
{
Thickness currentMargin = frameworkElement.Margin;
frameworkElement.Margin = new Thickness(currentMargin.Left, currentMargin.Top, value, currentMargin.Bottom);
}
}
public static double GetRight(UIElement element)
{
return 0;
}
public static readonly DependencyProperty BottomProperty = DependencyProperty.RegisterAttached(
"Bottom",
typeof(double),
typeof(Margin),
new PropertyMetadata(0.0));
public static void SetBottom(UIElement element, double value)
{
var frameworkElement = element as FrameworkElement;
if (frameworkElement != null)
{
Thickness currentMargin = frameworkElement.Margin;
frameworkElement.Margin = new Thickness(currentMargin.Left, currentMargin.Top, currentMargin.Right, value);
}
}
public static double GetBottom(UIElement element)
{
return 0;
}
}
用法:
<TextBlock Text="Test"
app:Margin.Top="{Binding MyValue}"
app:Margin.Right="{StaticResource MyResource}"
app:Margin.Bottom="20" />
在UWP中测试,但这适用于任何基于XAML的框架。好处是它们不会覆盖边距上的其他值,因此您也可以将它们组合在一起。
答案 5 :(得分:0)
使用转换器,下面的示例代码会将您绑定的双精度转换为厚度。它会将厚度的“顶部”设置为边界字段。您可以选择使用ConverterParameter来确定是否绑定到left,top,right或bottom。
<StackPanel Margin="{Binding TopMargin, Converter={StaticResource MyThicknessConverter}">
public class ThicknessSingleValueConverter : IValueConverter
{
override Convert(...)
{
return new Thickness(0, (double)object, 0, 0);
}
//etc...
答案 6 :(得分:0)
这是一个很好的解决方案:
public class Nifty
{
private static double _tiny;
private static double _small;
private static double _medium;
private static double _large;
private static double _huge;
private static bool _resourcesLoaded;
#region Margins
public static readonly DependencyProperty MarginProperty =
DependencyProperty.RegisterAttached("Margin", typeof(string), typeof(Nifty),
new PropertyMetadata(string.Empty,
new PropertyChangedCallback(OnMarginChanged)));
public static Control GetMargin(DependencyObject d)
{
return (Control)d.GetValue(MarginProperty);
}
public static void SetMargin(DependencyObject d, string value)
{
d.SetValue(MarginProperty, value);
}
private static void OnMarginChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement ctrl = d as FrameworkElement;
if (ctrl == null)
return;
string Margin = (string)d.GetValue(MarginProperty);
ctrl.Margin = ConvertToThickness(Margin);
}
private static Thickness ConvertToThickness(string Margin)
{
var result = new Thickness();
if (!_resourcesLoaded)
{
_tiny = (double)Application.Current.FindResource("TinySpace");
_small = (double)Application.Current.FindResource("SmallSpace");
_medium = (double)Application.Current.FindResource("MediumSpace");
_large = (double)Application.Current.FindResource("LargeSpace");
_huge = (double)Application.Current.FindResource("HugeSpace");
_resourcesLoaded = true;
}
result.Left = CharToThickness(Margin[0]);
result.Top = CharToThickness(Margin[1]);
result.Bottom = CharToThickness(Margin[2]);
result.Right = CharToThickness(Margin[3]);
return result;
}
private static double CharToThickness(char p)
{
switch (p)
{
case 't':
case 'T':
return _tiny;
case 's':
case 'S':
return _small;
case 'm':
case 'M':
return _medium;
case 'l':
case 'L':
return _large;
case 'h':
case 'H':
return _huge;
default:
return 0.0;
}
}
#endregion
}
如果将此代码添加到命名空间并定义以下大小:
<system:Double x:Key="TinySpace">2</system:Double>
<system:Double x:Key="SmallSpace">5</system:Double>
<system:Double x:Key="MediumSpace">10</system:Double>
<system:Double x:Key="LargeSpace">20</system:Double>
<system:Double x:Key="HugeSpace">20</system:Double>
然后你可以创建Tiny,Small,Medium,Large&amp;像这样巨大的利润:
local:Nifty.Margin="H000"
或
local:Nifty.Margin="_S_S"
然后代码将根据您的资源创建边距。
答案 7 :(得分:0)
也许我“迟到了”,但不喜欢任何提供的解决方案,在我看来,最简单和最干净的解决方案是在ViewModel中定义Thickness属性(或者你绑定的任何东西)然后绑定那个财产。像这样:
public class ItemViewModel
{
public Thickness Margin { get; private set }
public ItemViewModel(ModelClass model)
{
/// You can calculate needed margin here,
/// probably depending on some value from the Model
this.Margin = new Thickness(0,model.TopMargin,0,0);
}
}
然后XAML很简单:
<StackPanel Margin="{Binding Margin}">
答案 8 :(得分:0)
这是一种简单的方法,无需编写转换器或硬编码边距值。首先,在Window(或其他控件)资源中定义以下内容:
<Window.Resources>
<!-- Define the default amount of space -->
<system:Double x:Key="Space">10.0</system:Double>
<!-- Border space around a control -->
<Thickness
x:Key="BorderSpace"
Left="{StaticResource Space}"
Top="{StaticResource Space}"
Right="{StaticResource Space}"
Bottom="{StaticResource Space}"
/>
<!-- Space between controls that are positioned vertically -->
<Thickness
x:Key="TopSpace"
Top="{StaticResource Space}"
/>
</Window.Resources>
请注意,system
定义为xmlns:system="clr-namespace:System;assembly=mscorlib"
。
现在您可以按如下方式使用这些资源:
<Grid
Margin="{StaticResource BorderSpace}"
>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button
Grid.Row="0"
Content="Button 1"
/>
<Button
Grid.Row="1"
Content="Button 2"
Margin="{StaticResource TopSpace}"
/>
</Grid>
现在,如果您想更改控件之间的默认空间,只需在一个位置更改它。
答案 9 :(得分:0)
我使用绑定到Margin(RelativeSource Self)的ValueConverter并解析ConverterParameter,给出为“top:123; left:456”。
转换器仅覆盖参数给出的边距。
public class MarginConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is Thickness)) return new Thickness();
Thickness retMargin = (Thickness) value;
List<string> singleMargins = (parameter as string)?.Split(';').ToList() ?? new List<string>();
singleMargins.ForEach(m => {
switch (m.Split(':').ToList()[0].ToLower().Trim()) {
case "left":
retMargin.Left = double.Parse(m.Split(':').ToList()[1].Trim());
break;
case "top":
retMargin.Top = double.Parse(m.Split(':').ToList()[1].Trim());
break;
case "right":
retMargin.Right = double.Parse(m.Split(':').ToList()[1].Trim());
break;
case "bottom":
retMargin.Bottom = double.Parse(m.Split(':').ToList()[1].Trim());
break;
}
}
);
return retMargin;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML
<TextBlock Margin="{Binding RelativeSource={RelativeSource Self},
Path=Margin,
Converter={StaticResource MarginConverter},
ConverterParameter='top:0'}"
Style="{StaticResource Header}"
Text="My Header" />
TextBlock将使用Style提供的保证金,但Margin-Top除外,它将被0覆盖。
玩得开心!
答案 10 :(得分:-1)
我认为您可以使用MSDN中的属性语法:
<object.Margin>
<Thickness Top="{Binding Top}"/>
</object.Margin>
比你不需要任何转换器
但Top不是DependancyProperty - 回到转换器
答案 11 :(得分:-3)
通过指定类似下面的代码示例,可以做到这一点。
<StackPanel Margin=",10,,">
不幸的是,默认情况下,这种功能在WPF中似乎并不存在,这是一种耻辱,因为它要求开发人员以一种后来使应用程序外观或主题化更加困难的方式对已知的默认值进行硬编码。
此时我能想到的最佳解决方案是使用转换器,但是为了引入这个转换器而必须生成的额外代码量并不理想。