Как установить верхнее поле только в XAML? - PullRequest
43 голосов
/ 22 августа 2009

Я могу установить поля индивидуально в код , но как мне это сделать в XAML, например? как мне это сделать:

псевдокод:

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

Ответы [ 12 ]

54 голосов
/ 23 августа 2009

Разве это не то, что вы ищете?

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

Первым значением является Левое поле, затем Верх, затем Право и последнее, но не менее важное значение.

Я не уверен, если вы хотите связать это с чем-то, но если нет, это сработает.

36 голосов
/ 22 августа 2009

Ключ должен понять, что установка его в коде так:

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

эквивалентно:

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

Вы не можете установить только одно значение в экземпляре Thickness с помощью либо кода, либо XAML . Если вы не установите некоторые значения, они будут неявно равны нулю. Поэтому вы можете просто сделать это, чтобы преобразовать принятый пример кода в вашем другом вопросе в эквивалент 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.

20 голосов
/ 07 июля 2012

Это относится к поправкам WPF:

  1. Я WPF, и вы будете использовать меня при кодировании приложений для Windows - в конце концов.
  2. Не используйте другие технологии - я не буду кроссплатформенным, но попробую с SL.
  3. Если вы собираетесь использовать меня - убедитесь, что знаете, что делаете.
  4. Каждые 7 дней, часов или минут кодирования я заставляю вас делать перерыв, чтобы перейти на SO.
  5. Уважайте формы окон.
  6. MVVM -> INPC, INCC -> вы можете использовать его или использовать с гневом - ваш выбор!
  7. Не взаимодействовать с другими приложениями.
  8. Вы также должны заплатить за смесь.
  9. Вы не сможете динамически устанавливать позицию элемента, используя привязку либо прикрепленного свойства, либо поля, без написания нескольких строк кода позади.

  10. Не сравнивайте эту технологию с другими.

Ваша проблема указана в # 9.

2 голосов
/ 05 сентября 2015

Только что написал несколько прикрепленных свойств, которые позволят легко установить отдельное значение 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. Приятно то, что они не переопределяют другие значения на полях, так что вы также можете комбинировать их.

2 голосов
/ 22 августа 2009

Невозможно определить только верхнее поле с привязкой, поскольку Margin имеет тип Thickness, который не является объектом зависимости. Однако вы могли бы использовать 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>
0 голосов
/ 23 августа 2016

Я использую ValueConverter, привязанный к Margin (RelativeSource Self), и анализирую параметр 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 будет использовать поле, заданное стилем, за исключением Margin-Top, которое будет перезаписано 0.

Веселитесь вместе с ним!

0 голосов
/ 08 июля 2015

Вот простой способ сделать это без написания конвертеров или жестких значений полей. Сначала определите в своих ресурсах 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>

Теперь, если вы хотите изменить пространство по умолчанию между элементами управления, вам нужно изменить его только в одном месте.

0 голосов
/ 15 июля 2014

Возможно, я "опоздал на вечеринку", но мне не понравилось ни одно из предоставленных решений, и мне кажется, что самое простое и чистое решение - определить свойство Thickness во ViewModel (или что-либо, что вы связываете), а затем Bind это свойство. Примерно так:

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}">
0 голосов
/ 21 января 2011

Вот отличное решение:

        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>

Затем вы можете создать крошечные, маленькие, средние, большие и огромные поля следующим образом:

local:Nifty.Margin="H000"

или

local:Nifty.Margin="_S_S"

Код будет создавать поля на основе ваших ресурсов.

0 голосов
/ 28 января 2010

Хорошо бы иметь возможность сделать это, указав что-то вроде примера кода ниже.

<StackPanel Margin=",10,,">
1003

Лучшее решение, которое я могу придумать на данный момент, - это использование конвертера, но объем дополнительного кода, который вам нужно произвести, чтобы представить его, не идеален.

...