如何仅在XAML中设置顶部边距?

我可以在代码中单独设置边距,但是如何在XAML中执行此操作,例如,如何执行此操作:

伪代码:

<StackPanel Margin.Top="{Binding TopMargin}"> 

关键是要认识到,这样的代码设置:

 sp2.Margin = new System.Windows.Thickness{ Left = 5 }; 

相当于:

 sp2.Margin = new System.Windows.Thickness{ Left = 5, Top = 0, Right = 0, Bottom = 0 }; 

不能通过 代码或XAMLThickness实例中设置单个值。 如果你不设置一些值,它们将隐式地为零。 因此,您可以这样做,将您的其他问题中接受的代码示例转换为与XAML等效的代码:

 <StackPanel Margin="{Binding TopMargin, Converter={StaticResource MyConverter}}"/> 

MyConverter只返回一个Thickness ,它只设置Top ,其余所有值都为零。

当然,你可以编写你自己的控件,将这些单独的值作为依赖属性公开,使你的代码更清洁一点:

 <CustomBorder TopMargin="{Binding TopMargin}"> </CustomBorder> 

这不是你要找的吗?

 <StackPanel Margin="0,10,0,0" /> 

第一个值是左边距,然后是顶部,然后是右边,最后是但不是最底部。

我不确定你是否想把它绑定到某个东西上,但是如果没有,那就行了。

这属于WPF修正案:

  1. 我是WPF,最终你会在编写Windows应用程序时使用我。
  2. 不要使用其他技术 – 我不会跨平台,但我会尝试与SL。
  3. 如果你打算使用我 – 确保你知道你在做什么。
  4. 每7天或几小时或几分钟的编码,我会让你rest去SO。
  5. 尊重Windows窗体。
  6. MVVM – > INPC,INCC – >你可以使用它,或者你可以用它愤怒 – 你的select!
  7. 不要互动其他应用程序。
  8. 你也应该支付混合费用。
  9. 你不能dynamic地使用任何附加属性或边界的绑定来设置元素的位置,而不会在后面写几行代码。

  10. 不要把这个技术与其他人比较。

你的问题在#9列出。

您不能仅使用绑定来定义“上边距”,因为“ MarginThicknesstypes,不是依赖对象。 但是,您可以使用一个MultiValueConverter ,它需要4个边距值才能生成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> 

使用转换器,下面的示例代码将把您要绑定的双精度转换为厚度。 它会将厚度的“顶部”设置为绑定字段。 您可以select使用ConverterParameter来确定您是否绑定到左侧,顶部,右侧或底部。

 <StackPanel Margin="{Binding TopMargin, Converter={StaticResource MyThicknessConverter}"> 

 public class ThicknessSingleValueConverter : IValueConverter { override Convert(...) { return new Thickness(0, (double)object, 0, 0); } //etc... 

这是一个漂亮的解决scheme:

  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> 

然后,您可以创build这样的Tiny,Small,Medium,Large&Large margin:

 local:Nifty.Margin="H000" 

要么

 local:Nifty.Margin="_S_S" 

代码将根据您的资源创build利润率。

这是一个简单的方法,不用编写转换器或硬编码边界值。 首先,在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> 

现在,如果要更改控件之间的默认空间,则只需在一个位置更改它即可。

刚刚写了一些附加的属性,可以很容易地从绑定或静态资源中设置单独的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" /> 

在UWPtesting,但这应该适用于任何基于XAML的框架。 好的是他们不会覆盖保证金上的其他值,所以你可以把它们结合起来。

我使用绑定到Margin(RelativeSource Self)的ValueConverter并parsingConverterParameter,给出为“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将使用除了Margin-Top之外的由Style指定的Margin,它将被覆盖0。

玩得开心!

也许我是“迟到派对”,但不喜欢任何提供的解决scheme,在我看来,最简单和最干净的解决scheme是在ViewModel(或任何绑定)中定义厚度属性,然后绑定该属性。 像这样的东西:

 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}"> 

我以为你可以使用MSDN的属性语法:

  <object.Margin> <Thickness Top="{Binding Top}"/> </object.Margin> 

比你不需要任何转换器

但顶端不是依赖性 – 回到转换器

最好能通过指定类似下面的代码示例来做到这一点。

 <StackPanel Margin=",10,,"> 

不幸的是,这个function在WPF中默认情况下并不存在,这是一个耻辱,因为它需要开发人员以一种稍后使得皮肤或主题应用程序变得更加困难的方式对已知的默认值进行硬编码。

我现在所能想到的最好的解决scheme就是使用一个转换器,但是为了介绍这个,需要额外的代码量并不理想。