2012-08-08 9 views
6

多分、あなたが私にこれを理解させる手助けをすることができます:私は辞書とその辞書にバインドされているItemsControlを持っています。各エントリのキーはItemsControlの各Itemの内容を決定し、Valueは各Itemの幅を決定します。この大きな問題:幅はパーセンテージの値なので、たとえば、私のItemは親の20%のサイズでなければならないということがわかります。WPFの動的パーセンテージベースの幅

どうすればこの問題を解決できますか? グリッドはスターベースの幅で動作することができますが、グリッドの先頭にGridDefinitionを定義する必要があるため、ItemsControl.ItemTemplateでこれを行うことはできません。

現在のコード:

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

この上の任意のアイデア?これを解決する上品な方法はありますか?

ありがとうございます!

明確化:パーセンテージは、ItemControls親に基づいているはずです。

もう1つ:各アイテムはグリッドの1つの列であり、行ではありません。だから私はすべてのアイテムが同じ列にお互いにある必要があります。

ソリューション:あなたの助けを

おかげで、この問題は、多を使用してのItemsControlのActualWidthに結合することによって解決することができます。このようにして、ItemsControlのサイズが変わるたびにItemsも変更されます。グリッドは必要ありません。この解決策では、相対的な幅しか作成されませんが、項目の高さに同じ解決策を適用することはもちろん可能です。

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

コンバータ

XAML:これは、より完全な説明のために以下の短いバージョンダウンを参照され

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

をそして、それはトリックを行う必要があります!

+0

どのような親の20%ですか? ItemsControl、Labelなどの親ですか? – jtimperley

+0

「グリッドの始めにGridDefinitionを定義する必要がありますか?」とはどういう意味ですか?代わりに 'DataTemplate'であなたのグリッドを定義することができます - それは' ItemsPanelTemplate'にある必要はありません。 –

+0

こんにちは。私の編集を参照してください、20%はItemControls親(または親ラベル)の20%であるはずです。ラベルの親を両方向にストレッチすることができますので同じです。グリッドの始まりについて:はい、私はDataTemplateでグリッドを定義できますが、各Itemには独自のグリッドがあり、動作しませんか、ここで何かを監視していますか? – BlackWolf

答えて

7

間の空きスペースを分割、3列を作成する必要があると思います。

更新

MultiBindingがお手伝いします。

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)コンバータ:

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)ビューモデル:

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

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

4)コードビハインド:ここでサンプルです

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

MultiBindingActualWidthが変更された場合、バインディングターゲットを強制的に更新します。

+0

これは有望ですが、うまくいかないようです。 Converterが呼び出されると、まだ描画されていないため、ItemsControlのActualWidthは0になります。しかし、私にとっては良い方向に思えます。 – BlackWolf

+1

@BlackWolf、答えを更新しました。 – Dennis

+0

ありがとう、マルチバインドは魅力的に機能しました! ActualWidthが変更されたときにバインディングを手動で更新しようとしましたが、動作しませんでした。マルチバインディングではうまく動作します。清潔で素敵な解決策! – BlackWolf

0

Gridを入れてみてください。このGridには2つの列があります.1つは実際の内容で、もう1つは空白です。辞書のValueを使用して、おそらくIValueConverterを使用して、これらの列の幅をバインドできるはずです。

コンテンツを中心にする必要がある場合、あなたはあなたがIValueConverterを実装することができ、列0と2

+0

幅をバインドしないで、ColumnDefinitionのSharedSizeGroupプロパティを使用します。 – jtimperley

+0

私はこれが私の助けにはならないと私は思う、私の編集を参照してください。私は各項目を1つの列にする必要があり、すべての項目を互いに並べて配置する必要があるため、残念ながらスペースで作業することはできません。 – BlackWolf

0

グリッドを使用できないことが必要でした。

コンテンツをラップして幅/高さの動的な割合を追加できるContentControlを作成しました。

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