web-dev-qa-db-fra.com

Comment définir une marge supérieure uniquement en XAML?

Je peux définir les marges individuellement dans code mais comment puis-je le faire en XAML, par exemple. Comment puis-je faire cela:

PSEUDO-CODE:

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

La clé est de réaliser que le placer dans un code comme ceci:

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

est équivalent à:

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

Vous ne pouvez pas définir une seule valeur dans une instance Thickness par le biais de code ou XAML . Si vous ne définissez pas certaines des valeurs, elles seront implicitement égales à zéro. Par conséquent, vous pouvez simplement effectuer cette opération pour convertir l’exemple de code accepté dans votre autre question en un équivalent XAML:

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

MyConverter renvoie simplement une Thickness qui définit uniquement Top et laisse toutes les autres valeurs à zéro.

Bien sûr, vous pourriez écrire votre propre contrôle qui ne expose ces valeurs individuelles en tant que propriétés de dépendance pour rendre votre code un peu plus propre:

<CustomBorder TopMargin="{Binding TopMargin}">
</CustomBorder>
33
Kent Boogaart

N'est-ce pas ce que vous cherchez?

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

La première valeur est la marge de gauche, puis Haut, puis Droite et enfin le bas.

Je ne sais pas si vous voulez le lier à quelque chose, mais sinon, ça fonctionnera.

54
Carlo

Cela fait partie des amendements WPF:

  1. Je suis WPF et vous allez m'utiliser pour le codage d'applications Windows - éventuellement.
  2. N'utilisez pas d'autres technologies - je ne serai pas multi-plateforme mais j'essaierai d'utiliser SL.
  3. Si vous avez l'intention de m'utiliser, assurez-vous de savoir ce que vous faites.
  4. Tous les 7 jours ou heures ou minutes de codage, je vous ferai faire une pause pour vous rendre à SO.
  5. Respectez les formes de fenêtres.
  6. MVVM -> INPC, INCC -> vous pouvez l'utiliser ou vous pouvez l'utiliser avec colère - votre choix!
  7. Ne pas interop d'autres applications.
  8. Vous devrez payer pour le mélange aussi.
  9. Vous ne pourrez pas définir de manière dynamique la position d'un élément en utilisant la liaison d'une propriété ou d'une marge attachée sans écrire quelques lignes de code.

  10. Ne comparez pas cette technologie à d'autres.

Votre problème est répertorié au n ° 9.

20
G.Y

Nous venons d'écrire quelques propriétés attachées qui devraient faciliter la définition d'une valeur de marge individuelle à partir d'une ressource de liaison ou statique:

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

Usage:

<TextBlock Text="Test"
    app:Margin.Top="{Binding MyValue}"
    app:Margin.Right="{StaticResource MyResource}"
    app:Margin.Bottom="20" />

Testé en UWP, mais cela devrait fonctionner pour tout framework basé sur XAML. La bonne chose est qu'ils ne remplaceront pas les autres valeurs sur la marge, vous pouvez donc les combiner aussi.

1
RandomEngy

Vous ne pouvez pas définir uniquement la marge supérieure avec une liaison, car Margin est de type Thickness qui n'est pas un objet de dépendance. Cependant, vous pouvez utiliser une MultiValueConverter qui prendrait 4 valeurs de marge pour créer 1 objet d'épaisseur.

Convertisseur:

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>
1
Thomas Levesque

Peut-être suis-je «en retard pour le parti», mais je n’ai aimé aucune des solutions proposées, et il me semble que la solution la plus simple et la plus propre consiste à définir la propriété Thickness dans ViewModel (ou tout ce que vous liez), puis associez cette propriété. Quelque chose comme ça:

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);
  }
}

Et puis XAML est simple:

<StackPanel Margin="{Binding Margin}">
0
Antonio Bakula

Voici un moyen simple de le faire sans écrire de convertisseurs ou de valeurs de marge codées en dur. Commencez par définir les éléments suivants dans vos ressources Window (ou autre contrôle):

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

Notez que system est défini en tant que xmlns:system="clr-namespace:System;Assembly=mscorlib".

Maintenant, vous pouvez utiliser ces ressources comme suit:

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

Maintenant, si vous souhaitez modifier l'espace par défaut entre les contrôles, il vous suffit de le modifier à un seul endroit.

0
redcurry

J'utilise un ValueConverter lié à la marge (RelativeSource Self) et analyse le ConverterParameter, donné comme "top: 123; left: 456".

Le convertisseur écrase uniquement les marges données par le paramètre.

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 utiliserait la marge donnée par Style sauf la marge supérieure, qui sera écrasée par 0.

Aie du plaisir avec ça!

0
WPFGermany

Voici une solution astucieuse:

        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

    }

Si vous ajoutez ce code à votre espace de noms et définissez les tailles suivantes:

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

Vous pouvez ensuite créer des marges minuscules, petites, moyennes, grandes et énormes comme ceci:

local:Nifty.Margin="H000"

ou

local:Nifty.Margin="_S_S"

Le code créera alors des marges en fonction de vos ressources.

0
sambeau

Utilisez un convertisseur, l'exemple de code ci-dessous convertira le double auquel vous vous liez en une épaisseur. Il définira le "Haut" de l'épaisseur sur le champ lié. Vous pouvez éventuellement utiliser un ConverterParameter pour déterminer si vous vous connectez à gauche, en haut, à droite ou en bas.

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

.

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

    //etc...
0
NotDan