【问题标题】:How to Bind Sum of width of two control to MinWidth of another control如何将两个控件的宽度之和绑定到另一个控件的 MinWidth
【发布时间】:2015-10-01 22:30:51
【问题描述】:

我想设置网格列的最小宽度,它应该是标签的实际宽度和放置在列单元格内的另一个网格内的按钮控件的总和。我为此目的使用了一个 Converter 类,但要弄清楚 XAML 部分。 转换器在这里:

class StringSumtoIntConverter : IMultiValueConverter
{
    public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
    {
        int sum = 0;
        foreach (var item in value)
        {
            sum += System.Convert.ToInt32(item);
        }

        return sum;
    }
    //...Other implementations
}

我写到现在的XAML代码是:

xmlns:helperClasses="clr-namespace:EmbroidaryManagementSystem_V2._0.HelperClasses" <!--Import class-->

<helperClasses:StringSumtoIntConverter x:Key="StringSumtoIntConvert"/>  <!--Inside Window.Resources tag-->

<ColumnDefinition Width="48*"> <!--Inside Grid-->
    <ColumnDefinition.MinWidth>
        <MultiBinding Converter="{StaticResource StringSumtoIntConvert}">
            <Binding Path=""></Binding>
        </MultiBinding>
    </ColumnDefinition.MinWidth>
</ColumnDefinition>

XAML 的完整实现在这里:

<Window x:Class="EmbroidaryManagementSystem_V2._0.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:EmbroidaryManagementSystem_V2._0.View"
    xmlns:y="clr-namespace:EmbroidaryManagementSystem_V2._0.ViewModel.CollectionsViewModel"
    xmlns:helperClasses="clr-namespace:EmbroidaryManagementSystem_V2._0.HelperClasses"
    mc:Ignorable="d"
    Title="MainWindow" Height="655.512" Width="1120.159" FontSize="24" WindowStartupLocation="CenterScreen"
    >
<Window.Resources>
    <helperClasses:StringSumtoIntConverter x:Key="StringSumtoIntConvert"/>
</Window.Resources>
<Window.DataContext>
    <y:ClientCollectionVM/>
</Window.DataContext>
<Grid HorizontalAlignment="Left" Height="622" VerticalAlignment="Top" Width="1110" Background="#FFD6DBE9">
    <Grid.RowDefinitions>
        <RowDefinition Height="89*"/>
        <RowDefinition Height="39*"/>
        <RowDefinition Height="494*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="137*"/>
        <ColumnDefinition Width="48*">
            <ColumnDefinition.MinWidth>
                <MultiBinding Converter="{StaticResource StringSumtoIntConvert}">
                    <Binding Path=""></Binding>
                </MultiBinding>
            </ColumnDefinition.MinWidth>
        </ColumnDefinition>
    </Grid.ColumnDefinitions>
    <GridSplitter x:Name="gridSplitter" Grid.Column="1" HorizontalAlignment="Left" Height="533" Grid.Row="1" 
                  VerticalAlignment="Top" Width="2" Grid.RowSpan="2"/>
    <Grid Grid.Column="1" Height="35" Background="#FF657695" 
          Grid.Row="1" VerticalAlignment="Top">
        <Label x:Name="lblNotificationsHeader" Content="Notifications" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="14.667" Height="30" Foreground="#FFEBF0EE"/>
        <Button x:Name="btnNotificationsClose" Content="X" 
                Margin="0,5,8,0" VerticalAlignment="Top" Width="20" FontFamily="Verdana" HorizontalAlignment="Right" Background="Transparent" FontSize="13.333" Foreground="Black"/>
    </Grid>
</Grid>

还有一个错误提示:无法将“EmbroidaryManagementSystem_V2._0.ViewModel.CollectionsViewModel.ClientCollectionVM”类型的对象转换为“System.IConvertible”类型。 在行:

<MultiBinding Converter="{StaticResource StringSumtoIntConvert}">
                        <Binding Path=""></Binding>
                    </MultiBinding>

我不知道为什么。

【问题讨论】:

    标签: c# wpf xaml casting multibinding


    【解决方案1】:

    您需要在 MultiBinding 中向转换器提供值。

    <MultiBinding Converter="{StaticResource StringSumtoIntConvert}">
        <Binding ElementName="lblNotificationsHeader" Path="ActualWidth" />
        <Binding ElementName="btnNotificationsClose" Path="ActualWidth" />
    </MultiBinding>
    

    错误是因为 Path 为空,这意味着它正在获取 DataContext 并尝试将其传递给无法将 ClientCollectionVM 转换为 int 的转换器。

    我不能 100% 确定您将如何处理所有这些,但您可能可以取消转换器并执行以下操作:

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

    然后该列将扩展以适应其内容。当然,您可能希望将内部 Grid 更改为具有水平方向的 StackPanel:

    <StackPanel Grid.Row="1"
                    Grid.Column="1"
                    Height="35"
                    VerticalAlignment="Top"
                    Background="#FF657695"
                    Orientation="Horizontal">
            <Label x:Name="lblNotificationsHeader"
                   Height="30"
                   HorizontalAlignment="Left"
                   VerticalAlignment="Top"
                   Content="Notifications"
                   FontSize="14.667"
                   Foreground="#FFEBF0EE" />
            <Button x:Name="btnNotificationsClose"
                    Margin="0,5,8,0"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Top"
                    Background="Transparent"
                    Content="X"
                    FontFamily="Verdana"
                    FontSize="13.333"
                    Foreground="Black" />
        </StackPanel>
    

    要么这样,要么给内部 Grid 它自己的列定义,以将标签和按钮分开。

    更新

    根据您添加 GridSplitter 的更新,我提供了一个完整示例,说明如何实现您的目标。

    请记住,在您的示例中,无论您向哪个方向拖动,GridSplitter 都会真正调整左列的大小,而右列只是在扩展以填充。所以我们的目标不是在你的右栏有一个MinWidth,而是在我们的左栏有一个MaxWidth

    对转换器进行快速更改,因为 ActualWidthActualHeight 是双精度数,让我们将转换器更改为使用双精度数而不是整数,这样我们就不会出现杂散像素。

    public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
        {
            double sum = 0;
            foreach (var item in value)
            {
                sum += System.Convert.ToDouble(item);
            }
    
            return sum;
        }
    

    我们将把 GridSplitter 放在它自己的列中,将列宽设置为 "2",将第一个 ColumnDefinition 的宽度设置为 "4*",第三个设置为 "*""4*" 实际上只是一个随机数,星号大小取决于百分比,所以 4 并不总是有效,它只是在这种情况下起作用。

    我们还需要在内部网格中添加一些列,我们将为它们命名并使用这些元素来获取我们的 MultiBinding 的宽度。我们这样做而不是从元素本身获取宽度,因为 ActualWidth 不包括边距。

    <Grid Background="#FFD6DBE9">
        <Grid.RowDefinitions>
            <RowDefinition Height="89*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="494*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="4*" />
            <ColumnDefinition Width="2" />
            <ColumnDefinition Width="*">
                <ColumnDefinition.MinWidth>
                    <MultiBinding Converter="{StaticResource StringSumtoIntConvert}">
                        <Binding ElementName="cdNotificationHeaderLabel" Path="ActualWidth" />
                        <Binding ElementName="cdNotificationHeaderButton" Path="ActualWidth" />
                    </MultiBinding>
                </ColumnDefinition.MinWidth>
            </ColumnDefinition>
        </Grid.ColumnDefinitions>
        <GridSplitter x:Name="gridSplitter"
                      Grid.Row="1"
                      Grid.RowSpan="2"
                      Grid.Column="1"
                      Width="2"
                      ResizeBehavior="PreviousAndNext" />
        <Grid Grid.Row="1"
              Grid.Column="2"
              Height="35"
              Background="#FF657695">
            <Grid.ColumnDefinitions>
                <ColumnDefinition x:Name="cdNotificationHeaderLabel" Width="Auto" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition x:Name="cdNotificationHeaderButton" Width="Auto" />
            </Grid.ColumnDefinitions>
            <Label x:Name="lblNotificationsHeader"
                   Margin="0 0 5 0"
                   HorizontalAlignment="Left"
                   VerticalAlignment="Center"
                   Content="Notifications"
                   FontSize="14.667"
                   Foreground="#FFEBF0EE" />
            <Button x:Name="btnNotificationsClose"
                    Grid.Column="2"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Center"
                    Background="Transparent"
                    Content="X"
                    FontFamily="Verdana"
                    FontSize="13.333"
                    Foreground="Black" />
        </Grid>
    </Grid>
    

    综上所述,我建议从 GridSplitter 派生一个自定义控件并以这种方式处理它,但以上内容应该有助于您启动和运行。

    【讨论】:

    • 它工作了,但现在出现了另一个问题。实际上列之间有一个分隔符(我重新添加了之前在编辑中错误地删除的代码)。问题是当我将大小更改为右侧并且通知标签超出 Grid 的边界时,它的 ActualWidth 减小(到目前为止我已经注意到使用调试器)并且 Converter 被重复调用,每次都减小宽度。我希望将此 ActualWidth 固定为初始值。
    • 将第一列和第二列的宽度设置为“*”和“自动”没有帮助。我也尝试设置内部网格的MinWidth,它也没有帮助。我将标签和按钮放在同一列和同一行中,只是因为没有明显的需要分开它们。
    • 嗯,我缺少的主要概念是 GridSplitter 的水平和垂直对齐方式。我已按照您的建议将 GridSplitter 放在自己的位置,然后将其两个对齐方式设为“中心”。这样我就能够控制第一列和第三列的大小,毕竟我还必须在第一列中保持 TabControl 的 MinWidth 。所以非常感谢您的帮助。我在这个视图中避免了任何代码的事情,因为我是 WPF 的新手,我只是想特别在 XAML 中进行试验。
    • 我所做的与您的代码不同的另一件事是,(当它停止调整大小时,我希望 lblNotificationsHeader 和 btnNotificationsClo​​se 之间的空间接近“零”,内部网格列的实际宽度没有正确执行)我将内部网格第一列的MinWidth设置为lblNotificationsHeader的Width,第二列的Width设置为Auto,然后将绑定设置为cdNotificationHeaderLabel的MinWidth和cdNotificationHeaderButton的ActualWidth。这样它就像一个魅力。再次感谢您的帮助。
    • 很高兴你让它工作了,这就是我喜欢 WPF 的地方,通常有很多方法可以做某事。
    【解决方案2】:

    您没有在绑定中指定任何内容,因此它在您的数据上下文(即 VM)中传递。 VM 不能转换为 int。您需要传入 {Binding ElementName=btnNotificationsClo​​se, Path=ActualWidth} 和 lblNotificationsHeader。

    不过,老实说,听起来你把事情复杂化了。网格行/列不可调整大小,除非您使用您不使用的拆分器。

    【讨论】:

    • 其实我用的是splitter,我只是在编辑的时候把它去掉了,只是想简单点。很抱歉给您带来不便。
    • 是的,它奏效了。 Richard Preziosi 也给出了同样的回答。
    • 现在又出现了一个问题。实际上列之间有一个分隔符(我重新添加了之前在编辑中错误删除的代码)。问题是当我将大小更改为右侧并且通知标签超出 Grid 的边界时,它的 ActualWidth 减小(到目前为止我已经注意到使用调试器)并且 Converter 被重复调用,每次都减小宽度。我希望将此 ActualWidth 固定为初始值。
    • @user3357779,是的,这是 GridSplitter 的一个已知问题。您需要创建一个派生类并抓住鼠标拖动以防止它向右移动太远。
    • Richard Preziosi 在他的更新中的工作无需派生类并在代码中处理事件。无论如何感谢您的帮助。
    猜你喜欢
    • 2011-06-14
    • 1970-01-01
    • 2012-10-16
    • 2011-11-14
    • 2012-03-24
    • 1970-01-01
    • 1970-01-01
    • 2015-02-21
    • 2011-12-14
    相关资源
    最近更新 更多