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}">
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}}"/>
où 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>
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.
Cela fait partie des amendements WPF:
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.
Ne comparez pas cette technologie à d'autres.
Votre problème est répertorié au n ° 9.
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.
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>
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}">
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.
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!
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.
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...