C# 如何仅在XAML中设置上边距?

C# 如何仅在XAML中设置上边距?,c#,wpf,xaml,margins,C#,Wpf,Xaml,Margins,我可以在中单独设置边距,但如何在XAML中设置边距,例如,如何设置边距: 伪代码: <StackPanel Margin.Top="{Binding TopMargin}"> 您不能仅使用绑定定义上边距,因为边距的类型为厚度,而不是依赖项对象。但是,您可以使用一个多值转换器,它需要4个边距值来生成1个厚度对象 转换器: public class ThicknessMultiConverter : IMultiValueConverter { #region IMultiVa

我可以在中单独设置边距,但如何在XAML中设置边距,例如,如何设置边距:

伪代码:

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

您不能仅使用绑定定义上边距,因为
边距
的类型为
厚度
,而不是依赖项对象。但是,您可以使用一个
多值转换器
,它需要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>

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

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

关键是要意识到在代码中设置它如下:

sp2.Margin = new System.Windows.Thickness{ Left = 5 };
local:Nifty.Margin="H000"
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);
  }
}
相当于:

sp2.Margin = new System.Windows.Thickness{ Left = 5, Top = 0, Right = 0, Bottom = 0 };
不能通过代码或XAML在
Thickness
实例中仅设置一个值。如果不设置某些值,它们将隐式为零。因此,您可以这样做,将另一个问题中接受的代码示例转换为XAML等效代码:

<StackPanel Margin="{Binding TopMargin, Converter={StaticResource MyConverter}}"/>
与自定义控件相比,更好的选择是编写附加属性,并使用依赖属性设置器中的上述代码更改厚度。下面的代码可用于所有有余量的控件

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属性上获得通知更改。

这不是您要寻找的吗

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

第一个值是左边距,然后是上边距,然后是右边距,最后是下边距


我不确定您是否要将其绑定到某个对象,但如果不想,那就行了。

我想您可以使用属性语法,从:


这样你就不需要任何转换器了


但是顶部不是dependencProperty-返回到转换器(back-to-converter)

最好是通过指定类似下面的代码示例的内容来实现这一点

<StackPanel Margin=",10,,">

不幸的是,WPF中默认情况下似乎不存在此功能,这是一个遗憾,因为它要求开发人员硬编码已知的默认值,这样以后就更难对应用程序进行蒙皮或主题化


目前我能想到的最好的解决方案是使用转换器,但引入转换器所需的额外代码量并不理想。

这里有一个很好的解决方案:

        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>


代码将根据您的资源创建边距。

这属于WPF修订:

  • 我是WPF,在为windows应用程序编写代码时,您最终会使用我
  • 不要使用其他技术-我不会跨平台,但我会尝试使用SL
  • 如果你想利用我-确保你知道你在做什么
  • 每7天或每小时或每分钟的编码,我会让你休息一下去SO
  • 尊重windows窗体
  • MVVM->INPC,INCC->您可以使用它,也可以愤怒地使用它-您的选择
  • 不要与其他应用互操作
  • 你也应该支付混合的费用
  • 如果不在后面写几行代码,您将无法使用附加属性或边距的绑定动态设置元素的位置

  • 不要将此技术与其他技术进行比较

  • 您的问题列在第9页。

    也许我“迟到了”,但不喜欢提供的任何解决方案,在我看来,最简单、最干净的解决方案是在ViewModel中定义厚度属性(或您正在绑定的任何内容),然后绑定该属性。大概是这样的:

    sp2.Margin = new System.Windows.Thickness{ Left = 5 };
    
    local:Nifty.Margin="H000"
    
    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}">
    

    这里有一种简单的方法,可以在不编写转换器或硬编码边距值的情况下执行此操作。首先,在窗口(或其他控件)资源中定义以下内容:

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

    现在,如果要更改控件之间的默认间距,只需在一个位置进行更改。

    刚刚编写了一些附加属性,这些属性可以轻松地从绑定或静态资源设置单个边距值:

    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的框架。好在它们不会覆盖边距上的其他值,因此您也可以将它们组合起来。

    我使用绑定到边距的值转换器(RelativeSource Self)并解析转换器参数,如“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();
        }
    }
    
    公共类MarginConverter:IValueConverter
    {
    公共对象转换(对象值、类型targetType、对象参数、CultureInfo区域性)
    {
    如果(!(值为厚度))返回新厚度();
    厚度边缘=(厚度)值;
    List singleMargins=(参数为字符串)?.Split(“;”).ToList()?new List();
    singleMargins.ForEach(m=>{
    开关(m.Split(“:”).ToList()[0].ToLower().Trim()){
    案例“左”:
    retMargin.Left=double.Parse(m.Split(“:”).ToList()[1].Trim());
    打破
    案例“顶部”:
    retMargin.Top=double.Parse(m.Split(“:”).ToList()[1].Trim());
    打破
    案例“权利”:
    retMargin.Right=double.Parse(m.Split(':').ToList()[1].Trim());
    打破
    案例“底部”:
    retMargin.Bottom=double.Parse(m.Split(“:”).ToList()[1].Trim());
    打破
    
    <TextBlock Text="Test"
        app:Margin.Top="{Binding MyValue}"
        app:Margin.Right="{StaticResource MyResource}"
        app:Margin.Bottom="20" />
    
    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();
        }
    }
    
    <TextBlock Margin="{Binding RelativeSource={RelativeSource Self}, 
                        Path=Margin, 
                        Converter={StaticResource MarginConverter}, 
                        ConverterParameter='top:0'}" 
    Style="{StaticResource Header}" 
    Text="My Header" />