working not left c# wpf xaml margins

c# - not - ¿Cómo establecer un margen superior solo en XAML?



wpf padding (12)

Puedo establecer márgenes de forma individual en el code pero ¿cómo lo hago en XAML, por ejemplo, cómo hago esto?

PSEUDO-CÓDIGO:

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


¿No es esto lo que estás buscando?

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

El primer valor es el margen izquierdo, luego el superior, luego el derecho y, por último, pero no el inferior, el inferior.

No estoy seguro si quieres vincularlo a algo, pero si no, eso funcionará.


Aquí hay una forma simple de hacerlo sin escribir convertidores ni valores de margen de codificación. Primero, defina lo siguiente en sus recursos de Ventana (u otro control):

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

Tenga en cuenta que el system se define como xmlns:system="clr-namespace:System;assembly=mscorlib" .

Ahora puede usar estos recursos de la siguiente manera:

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

Ahora, si desea cambiar el espacio predeterminado entre controles, solo necesita cambiarlo en un lugar.


Aquí hay una solución ingeniosa:

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 agrega este código a su espacio de nombre y define los siguientes tamaños:

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

A continuación, puede crear márgenes pequeños, pequeños, medianos, grandes y enormes como este:

local:Nifty.Margin="H000"

o

local:Nifty.Margin="_S_S"

El código creará márgenes basados ​​en sus recursos.


Esto pertenece a las enmiendas de WPF:

  1. Soy WPF y me usarás al codificar aplicaciones de Windows, eventualmente.
  2. No use otras tecnologías. No seré multiplataforma, pero lo intentaré con SL.
  3. Si tiene la intención de usarme, asegúrese de saber lo que está haciendo.
  4. Cada 7 días u horas o minutos de codificación, te haré tomar un descanso para ir a SO.
  5. Respetar los formularios de Windows.
  6. MVVM -> INPC, INCC -> puedes usarlo o puedes usarlo con enojo - ¡tu elección!
  7. No interopere otras aplicaciones.
  8. Pagarás por la mezcla también.
  9. No podrá establecer una posición de un elemento de forma dinámica mediante el enlace de una propiedad adjunta o margen sin escribir algunas líneas de código.

  10. No compare esta tecnología con otros.

Su problema está en la lista en el n. ° 9.


La clave es darse cuenta de que configurarlo en un código como este:

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

es equivalente a:

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

No puede establecer un solo valor en una instancia de Thickness través de código o XAML . Si no establece algunos de los valores, serán implícitamente cero. Por lo tanto, puede hacer esto para convertir el ejemplo de código aceptado en su otra pregunta a un equivalente XAML:

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

donde MyConverter simplemente devuelve un Thickness que establece solo la parte Top y deja todos los demás valores como cero.

Por supuesto, puede escribir su propio control que exponga estos valores individuales como propiedades de dependencia para que su código sea un poco más limpio:

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


Lo que sería bueno es poder hacer esto especificando algo como el siguiente ejemplo de código.

<StackPanel Margin=",10,,">

Desafortunadamente, esta capacidad no parece existir de manera predeterminada en WPF y es una pena porque requiere que los desarrolladores codifiquen los valores predeterminados conocidos de una manera que luego hace que sea más difícil cambiar el aspecto o el tema de una aplicación.

La mejor solución que se me ocurre en este momento es usar un convertidor, pero la cantidad de código adicional que tiene que producir para introducir esto no es ideal.


No puede definir solo el margen superior con un enlace, porque el Margin es de tipo Thickness que no es un objeto de dependencia. Sin embargo, podría usar un MultiValueConverter que tomaría 4 valores de margen para hacer objetos 1 Thickness

Convertidor:

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>


Pensé que podría usar la sintaxis de la propiedad desde MSDN :

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

Entonces no necesitarás ningún convertidor

Pero el Top no es DependancyProperty - volver al convertidor


Quizás estoy "llegando tarde a la fiesta", pero no me gustó ninguna de las soluciones proporcionadas, y me parece que la solución más simple y limpia es definir la propiedad Grosor en ViewModel (o cualquier cosa que quieras vincular) y luego vincular esa propiedad. Algo como esto:

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

Y luego XAML es simple:

<StackPanel Margin="{Binding Margin}">


Simplemente escribí algunas propiedades adjuntas que deberían facilitar la configuración de un valor de Margen individual a partir de un recurso vinculante o estático:

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

Uso:

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

Probado en UWP, pero esto debería funcionar para cualquier marco basado en XAML. Lo bueno es que no anularán los otros valores en el margen, por lo que puede combinarlos también.


Use un convertidor, el siguiente código de muestra convertirá el doble al que está vinculando a un grosor. Establecerá el "Top" del grosor en el campo encuadernado. Opcionalmente, puede usar un parámetro convertidor para determinar si está vinculando a la izquierda, arriba, derecha o abajo.

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

.

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


Utilizo un ValueConverter vinculado a Margin (RelativeSource Self) y analizo el ConverterParameter, dado como "top: 123; left: 456".

El convertidor solo sobrescribe los márgenes proporcionados por parámetro.

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 usaría el Margin dado por Style excepto Margin-Top, que se sobrescribirá con 0.

¡Diviértete con eso!