如何将两个控件的宽度之和绑定到另一个控件的MinWidth

时间:2015-10-01 22:30:52

标签: c# wpf xaml casting multibinding

我想设置网格列的MinWidth,它应该是Label的实际宽度和放置在列的单元格内的另一个网格内的Button控件的总和。我为此目的使用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>

我不知道为什么。

2 个答案:

答案 0 :(得分:2)

您需要在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>

然后该列将展开以适合它的内容。当然,您可能希望将内部网格更改为具有水平方向的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提供它自己的列定义来将Label和Button分开。

<强>更新

根据您添加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*"<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> 实际上只是一个随机数,星级大小按百分比计算,所以4个赢了总是有效,在这种情况下就是这样。

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

String

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

答案 1 :(得分:1)

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

但是,老实说,这听起来好像是让事情变得复杂。除非您使用的是分割器,否则网格行/列不可调整大小。