2012-08-08 33 views
6

tal vez ustedes pueden ayudarme a resolver esto: tengo un Dictionary and ItemsControl que está vinculado a ese diccionario. La clave de cada entrada determina el contenido de cada elemento en ItemsControl y el valor determina el ancho de cada artículo. El gran problema con esto: el ancho es un valor porcentual, por lo que me dice que, por ejemplo, mi artículo debe ser el 20% de su tamaño principal.Ancho basado en porcentaje dinámico en WPF

¿Cómo puedo lograrlo? Sé que las grillas pueden trabajar con anchos basados ​​en estrellas, pero como tengo que definir la definición de grilla al comienzo de la grilla, no puedo hacer esto en ItemsControl.ItemTemplate.

código actual:

<ItemsControl ItemsSource="{Binding Distribution}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> 
     <ItemsControl.ItemsPanel> 
      <ItemsPanelTemplate> 
       <Grid IsItemsHost="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> 
      </ItemsPanelTemplate> 
     </ItemsControl.ItemsPanel> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
       <!-- I NEED THIS TO BE A CERTAIN PERCENTAGE IN WIDTH --> 
       <Label Content="{Binding Key.Text}" Foreground="{Binding Key.Color}"/> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 

¿Alguna idea sobre esto? ¿Hay alguna forma elegante de resolver esto?

Gracias!

Aclaraciones: ¡Se supone que el porcentaje se basa en el elemento primario de ObjectControls!

Y otra: Se supone que cada elemento es una columna de la cuadrícula, no una fila. Entonces necesito que todos los elementos estén uno al lado del otro en la misma fila.

La solución:

Gracias por su ayuda, este problema puede ser resuelto mediante el uso de unión múltiple y vinculación a la ActualWidth de ItemsControl. De esta forma, cada vez que ItemsControl cambia de tamaño, los elementos también cambian. Una cuadrícula no es necesaria. Esta solución solo crea un ancho relativo, pero la misma solución puede aplicarse a la altura de los artículos. Esta es una versión corta, para una explicación más detallada ver más abajo:

XAML:

<ItemsControl ItemsSource="{Binding Distribution}" Name="itemsControl" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> 
     <ItemsControl.ItemsPanel> 
      <ItemsPanelTemplate> 
       <StackPanel IsItemsHost="True" Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> 
      </ItemsPanelTemplate> 
     </ItemsControl.ItemsPanel> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
       <Label Content="{Binding Key.Text}" 
         Foreground="{Binding Key.Color}"> 
        <Label.Width> 
         <MultiBinding Converter="{StaticResource myConverter}"> 
          <Binding Path="Value"/> 
          <Binding Path="ActualWidth" ElementName="itemsControl"/> 
         </MultiBinding> 
        </Label.Width> 
       </Label> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 

convertidor:

class MyConverter : IMultiValueConverter 
{ 
    public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture) 
    { 
     //[1] contains the ItemsControl.ActualWidth we binded to, [0] the percentage 
     //In this case, I assume the percentage is a double between 0 and 1 
     return (double)value[1] * (double)value[0]; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Y que debe hacer el truco!

+0

20% de lo que padre? ¿El padre de ItemsControl, Label o algo más? – jtimperley

+0

¿Qué quiere decir con "Tengo que definir la definición de cuadrícula al comienzo de la cuadrícula?" En su lugar, podría definir su cuadrícula en 'DataTemplate'; no es necesario que esté en' ItemsPanelTemplate'. –

+0

Hola. Ver mi edición, el 20% se supone que es el 20% del elemento primario de ObjectControls (o las etiquetas padre. Como puedo hacer que las etiquetas se expandan en ambas direcciones, sería lo mismo). En cuanto al comienzo de la Grilla: Sí, puedo definir mi Grilla en la Plantilla de Datos, pero entonces cada Objeto tendría su propia Grilla, lo cual no funcionaría, ¿o estoy supervisando algo aquí? – BlackWolf

Respuesta

7

Puede implementar IValueConverter.

ACTUALIZACIÓN.

MultiBinding te ayudarán.Aquí está la muestra:

1) xaml:

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:WpfApplication1" 
     Title="MainWindow" Height="114" Width="404"> 
    <Grid> 
     <Grid.Resources> 
      <local:RelativeWidthConverter x:Key="RelativeWidthConverter"/> 
     </Grid.Resources> 

     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*"/> 
     </Grid.ColumnDefinitions> 

     <ItemsControl ItemsSource="{Binding}" 
         x:Name="itemsControl"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <Rectangle Fill="Green" Margin="5" Height="20" HorizontalAlignment="Left"> 
         <Rectangle.Width> 
          <MultiBinding Converter="{StaticResource RelativeWidthConverter}"> 
           <Binding Path="RelativeWidth"/> 
           <Binding Path="ActualWidth" ElementName="itemsControl"/> 
          </MultiBinding> 
         </Rectangle.Width> 
        </Rectangle> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 
    </Grid> 
</Window> 

2) convertidor:

public class RelativeWidthConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return ((Double)values[0] * (Double)values[1])/100.0; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

3) vista del modelo:

public class ViewModel : ViewModelBase 
{ 
    public ViewModel() 
    { 
    } 

    public Double RelativeWidth 
    { 
     get { return relativeWidth; } 
     set 
     { 
      if (relativeWidth != value) 
      { 
       relativeWidth = value; 
       OnPropertyChanged("RelativeWidth"); 
      } 
     } 
    } 
    private Double relativeWidth; 
} 

4) de código subyacente:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     DataContext = new[] 
     { 
      new ViewModel { RelativeWidth = 20 }, 
      new ViewModel { RelativeWidth = 40 }, 
      new ViewModel { RelativeWidth = 60 }, 
      new ViewModel { RelativeWidth = 100 }, 
     }; 
    } 
} 

MultiBinding fuerzas para actualizar el destino vinculante, cuando ActualWidth ha cambiado.

+0

Esto sonaba prometedor, pero no parece funcionar. Cuando se llama al convertidor, el ancho real de ItemsControl es 0, ya que aún no se ha dibujado. Me parece una buena dirección, sin embargo. – BlackWolf

+1

@BlackWolf, actualizó la respuesta. – Dennis

+0

Muchas gracias, ¡Multibinding funcionaba como un amuleto! Intenté actualizar manualmente el enlace cuando cambió el valor real, pero no funcionó. Con multibinding funciona bien. ¡Solución limpia y agradable! – BlackWolf

0

Puede tratar de poner un Grid en el ItemTemplate. Este Grid tendría 2 columnas: 1 para el contenido real y una para el espacio vacío. Debería poder vincular el ancho de estas columnas usando el Value de su diccionario, posiblemente con la ayuda de un IValueConverter.

Si el contenido tiene que estar centrado, que había necesidad de crear 3 columnas, dividiendo el espacio vacío entre las columnas 0 y 2.

+0

No vincule el ancho, use la propiedad SharedSizeGroup en ColumnDefinition. – jtimperley

+0

Creo que esto no me sirve de nada, ver mi edición. Necesito que cada elemento sea una columna y que todos los elementos se alineen uno al lado del otro, por lo tanto, no puedo trabajar con espacios, desafortunadamente. – BlackWolf

0

Tenía un requisito que no podía usar las rejillas.

Creé un ContentControl que me permite ajustar el contenido para agregar un porcentaje dinámico de ancho/alto.

/// <summary> 
/// This control has a dynamic/percentage width/height 
/// </summary> 
public class FluentPanel : ContentControl, IValueConverter 
{ 
    #region Dependencie Properties 

    public static readonly DependencyProperty WidthPercentageProperty = 
     DependencyProperty.Register("WidthPercentage", typeof(int), typeof(FluentPanel), new PropertyMetadata(-1, WidthPercentagePropertyChangedCallback)); 

    private static void WidthPercentagePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) 
    { 
     ((FluentPanel)dependencyObject).OnWidthPercentageChange(); 
    } 

    public int WidthPercentage 
    { 
     get { return (int)GetValue(WidthPercentageProperty); } 
     set { SetValue(WidthPercentageProperty, value); } 
    } 

    public static readonly DependencyProperty HeightPercentageProperty = 
     DependencyProperty.Register("HeightPercentage", typeof(int), typeof(FluentPanel), new PropertyMetadata(-1, HeightPercentagePropertyChangedCallback)); 

    private static void HeightPercentagePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) 
    { 
     ((FluentPanel)dependencyObject).OnHeightPercentageChanged(); 
    } 

    public int HeightPercentage 
    { 
     get { return (int)GetValue(HeightPercentageProperty); } 
     set { SetValue(HeightPercentageProperty, value); } 
    } 

    #endregion 

    #region Methods 

    private void OnWidthPercentageChange() 
    { 
     if (WidthPercentage == -1) 
     { 
      ClearValue(WidthProperty); 
     } 
     else 
     { 
      SetBinding(WidthProperty, new Binding("ActualWidth") { Source = Parent, Converter = this, ConverterParameter = true }); 
     } 
    } 

    private void OnHeightPercentageChanged() 
    { 
     if (HeightPercentage == -1) 
     { 
      ClearValue(HeightProperty); 
     } 
     else 
     { 
      SetBinding(HeightProperty, new Binding("ActualHeight") { Source = Parent, Converter = this, ConverterParameter = false }); 
     } 
    } 

    #endregion 

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     if ((bool)parameter) 
     { 
      // width 
      return (double)value * (WidthPercentage * .01); 
     } 
     else 
     { 
      // height 
      return (double)value * (HeightPercentage * .01); 
     } 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotSupportedException(); 
    } 
} 
Cuestiones relacionadas