【问题标题】:Dynamic percentage-based width in WPFWPF 中基于百分比的动态宽度
【发布时间】:2012-08-05 14:15:22
【问题描述】:

也许你们可以帮我解决这个问题:我有一个字典和一个绑定到该字典的 ItemsControl。每个条目的 Key 决定了 ItemsControl 中每个 Item 的内容,而 Value 决定了每个 Item 的宽度。最大的问题是:宽度是一个百分比值,所以它告诉我,例如,我的 Item 的大小需要是其父项的 20%。

我怎样才能做到这一点? 我知道网格能够使用基于星的宽度,但由于我必须在网格的开头定义 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 父级!

还有一个:每个项目都应该是网格的一列,而不是一行。所以我需要所有项目在同一行中彼此相邻。

解决方案

感谢您的帮助,这个问题可以通过使用多重绑定和绑定到 ItemsControl 的 ActualWidth 来解决。这样,每当 ItemsControl 的大小发生变化时,Items 也会发生变化。不需要网格。这个解决方案只创建一个相对宽度,但同样的解决方案当然可以应用于项目的高度。这是一个简短的版本,更详尽的解释见下文:

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>

转换器

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

这应该可以解决问题!

【问题讨论】:

  • 20% 什么父母? ItemsControl、Label 或其他东西的父级?
  • “我必须在网格的开头定义 GridDefinition”是什么意思?您可以在 DataTemplate 中定义您的网格 - 它不需要在 ItemsPanelTemplate 中。
  • 嗨。请参阅我的编辑,20% 应该是 ItemControls 父级(或标签父级)的 20%。因为我可以使 Labels 父级在两个方向上拉伸,所以它是相同的)。至于 Grid 的开头:是的,我可以在 DataTemplate 中定义我的 Grid,但是每个 Item 都会有自己的 Grid,这是行不通的,还是我在这里监督什么?

标签: c# wpf dynamic grid width


【解决方案1】:

我有一个不能使用网格的需求。

我创建了一个 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();
    }
}

【讨论】:

    【解决方案2】:

    你可以实现IValueConverter

    更新

    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 更改时强制更新绑定目标。

    【讨论】:

    • 这听起来很有希望,但似乎不起作用。调用 Converter 时,ItemsControl 的 ActualWidth 为 0,因为它尚未绘制。不过,这对我来说似乎是一个很好的方向。
    • 非常感谢,多重绑定就像一个魅力!当 ActualWidth 更改时,我尝试手动更新绑定,但它不起作用。使用多重绑定它可以正常工作。干净又漂亮的解决方案!
    【解决方案3】:

    您可以尝试在ItemTemplate 中添加Grid。这个Grid 将有 2 列:1 列用于实际内容,1 列用于空白空间。您应该能够使用字典的Value 绑定这些列的宽度,可能还可以借助IValueConverter

    如果内容需要居中,您需要创建 3 列,将第 0 列和第 2 列之间的空白区域分开。

    【讨论】:

    • 不要绑定宽度,使用 ColumnDefinition 上的 SharedSizeGroup 属性。
    • 我认为这对我没有帮助,请参阅我的编辑。我需要每个项目是一列,并且所有项目都需要彼此相邻对齐,因此很遗憾,我不能使用空格。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-31
    • 1970-01-01
    • 1970-01-01
    • 2013-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多