多绑定多选ListView

时间:2010-08-25 14:05:27

标签: wpf listview multi-select multibinding imultivalueconverter

好的,我在这里有一个奇怪的。我想弄清楚的是如何有一个listview,由ObservableCollection填充,更新另一个ListView,由另一个ObservableCollection填充,基于第一个listview的选择,然后使用valueconverter根据选择结合选中或取消选中复选框使用第二个listview中的当前项目。这部分我有点使用multibinding工作,但让我难倒的部分是当我在第二个listview中选中或取消选中一个项目时,我需要能够获取该事件以及该listview中当前所有选中的项目更新a数据库字段基于此。

我知道这可能没有多大意义,我正在努力使它更清晰,但下面是两个列表视图的xaml和转换器的代码。我可以看到,当我在secodn列表视图中选中或取消选中一个框时,转换器会尝试执行转换的ConvertBack方法,但如果我将其设置为仅返回null,则代码不再会爆炸,但是复选框会突出显示红色表示已发生验证错误。

我甚至不确定多绑定是什么方式去这里,我已经看过Josh Smith的多选列表视图,但是由于需要转换,我无法看到如何成功实现。

如果有人有任何想法我会非常感激。如果我没有非常清楚地解释我的需要,我也很抱歉,但是我希望有一个混乱的描述和代码,你可以看到我在哪里。

提前致谢!

第一个为第二个提供的ListView

<Grid>
                <ListView x:Name="listRule" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="3,3,3,3" ItemsSource="{Binding RuleListing}" exts:Selected.Command="{Binding RuleSelectedCommand}" SelectedIndex="0">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Path=DisplayName}" ToolTip="{Binding Path=Expression}" FontWeight="Bold"/>
                                <TextBlock Text=" ( "/>
                                <TextBlock Text="{Binding Description}" FontStyle="Italic" />
                                <TextBlock Text=" )"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </Grid>

带转换器和多重绑定的第二个ListView

<Grid HorizontalAlignment="Stretch">
                <Grid.Resources>
                    <converters:RuleToRoleBooleanConverter x:Key='RuleRoleConverter' />                        
                    <DataTemplate x:Key="RoleTemplate">
                        <Grid HorizontalAlignment="Stretch">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="1*" MinWidth="200"/>
                                <ColumnDefinition Width="20"/>
                            </Grid.ColumnDefinitions>
                            <TextBlock Text="{Binding RoleName}" HorizontalAlignment="Left" Margin="3,0,0,0" Grid.Column="0" />
                            <CheckBox HorizontalAlignment="Right" Margin="0,0,3,0" Grid.Column="1">
                                <CheckBox.IsChecked>
                                    <MultiBinding Converter="{StaticResource RuleRoleConverter}">
                                        <Binding ElementName="listRule" Path="SelectedItem" />
                                        <Binding Path="RoleName"/>
                                    </MultiBinding>
                                </CheckBox.IsChecked>
                            </CheckBox>
                        </Grid>
                    </DataTemplate>
                </Grid.Resources>
                <ListView Name="listRoles" ItemsSource="{Binding RoleListing}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
                          SelectionMode="Multiple" ItemTemplate="{StaticResource ResourceKey=RoleTemplate}">
                    <ListView.ItemContainerStyle>
                        <Style TargetType="{x:Type ListBoxItem}">
                            <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsRoleSelected}"/>
                        </Style>
                    </ListView.ItemContainerStyle>
                </ListView>
            </Grid>

价值转换器

public class RuleToRoleBooleanConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (values[0] != null && values[1] != null)
        {
            string expression = ((EliteExtenderRule)values[0]).Expression;
            string role = values[1].ToString();

            if (expression.Contains("R:*") || expression.Contains("R:" + role))
            {
                return true;
            }
        }
        return false;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;// new object[] { (bool)value, null };
    }}

1 个答案:

答案 0 :(得分:0)

我可能有一个问题的第一部分的解决方案,即“弄清楚如何有一个列表视图,由ObservableCollection填充,更新另一个ListView,由另一个ObservableCollection填充,基于第一个listview的选择”。

但之前,对于这部分问题,dit只是将目标ListView绑定在他选择的项ListView属性上才能满足你的需求?

     <ListView x:Name="listOne" Grid.Column="0" Width="50" Height="200" ItemsSource="{Binding SourceList}" SelectionMode="Extended"  />
    <ListView x:Name="listTwo" Grid.Column="1" Width="50" Height="200" ItemsSource="{Binding ElementName=listOne, Path=SelectedItems}" />

如果没有,我在最近的项目中遇到了类似的问题,我想出了一个应用于源列表的行为。

假设你有一个类似于你视图的DataContext的视图模型:

    public ObservableCollection<BusinessAdapter> SourceList { get; private set; }
    public ObservableCollection<BusinessAdapter> TargetList { get; private set; }
    public Window1()
    {
        InitializeComponent();
        DataContext = this;
        SourceList = new ObservableCollection<BusinessAdapter>();
        TargetList = new ObservableCollection<BusinessAdapter>();

        for (int i = 0; i < 50; i++)
        {
            SourceList.Add(new BusinessAdapter { BusinessProperty = "blabla_" + i });
        }
    }

在你看来,你有类似的东西:

    <ListView x:Name="listOne" Grid.Column="0" Width="50" Height="200" ItemsSource="{Binding SourceList}" SelectionMode="Extended" />
    <ListView x:Name="listTwo" Grid.Column="1" Width="50" Height="200" ItemsSource="{Binding TargetList}" />

然后,您可以在源列表框中附加此行为,并将目标视图模型源传递给它。

==&GT;行为:

public class ListBoxMultiSelectionBehavior
{
    public static IList<BusinessAdapter> GetTargetList(DependencyObject obj)
    {
        return (IList<BusinessAdapter>)obj.GetValue(TargetListProperty);
    }

    public static void SetTargetList(DependencyObject obj, IEnumerable<BusinessAdapter> value)
    {
        obj.SetValue(TargetListProperty, value);
    }

    // Using a DependencyProperty as the backing store for Adapter.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TargetListProperty =
        DependencyProperty.RegisterAttached("TargetList", typeof(IList<BusinessAdapter>), typeof(ListBoxMultiSelectionBehavior), new UIPropertyMetadata(null, OnListChanged));

    /// <summary>
    /// Model List changed callback
    /// </summary>
    /// <param name="d"></param>
    /// <param name="e"></param>
    private static void OnListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var mSelector = d as ListView ;

        if (mSelector != null)
        {
            mSelector.SelectionChanged -= MSelectorSelectionChanged;

            //Multiple or Extented selection mode are mandatory
            if (mSelector.SelectionMode == SelectionMode.Single)
            {
                return;
            }
            mSelector.SelectedItems.Clear();

            //"binding" => model to view
            //get the model's list
            var a = GetTargetList(d);
            if (a != null)
            {
                //for each model in the list
                foreach (var ba in a)
                {
                    //in the listbox items collection
                    foreach (var item in mSelector.Items)
                    {
                        //find the correspondance and if found
                        if (((BusinessAdapter)item).BusinessProperty == ba.BusinessProperty)
                        {
                            //add item to selected items
                            mSelector.SelectedItems.Add(item);

                        }
                    }

                }
            }

            //"binding" => view to model
            //subscribe to changes in selection  in the listbox
            mSelector.SelectionChanged += MSelectorSelectionChanged;
        }
    }



    static void MSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e != null)
        {
            var adapter = GetTargetList(sender as DependencyObject);
            var list = adapter;//copy

            if (e.RemovedItems.Count > 0 /*&& e.RemovedItems.Count != list.Count*/)
            {
                foreach (var ba in e.RemovedItems.Cast<BusinessAdapter>())
                {
                    list.Remove(ba);
                }
            }
            if (e.AddedItems.Count > 0)
            {
                foreach (var ba in e.AddedItems.Cast<BusinessAdapter>())
                {
                    list.Add(ba);
                }

            }
        }

    }
}

它的作用基本上是在初始化时,他检索目标列表项并填充源列表(单向),并订阅列表框源选择更改事件以填充目标列表(另一种方式)。然后,在项目添加到行为中时,目标列表框会更新。

新的XAML看起来就像那样:

    <ListView x:Name="listOne" Grid.Column="0" Width="50" Height="200" ItemsSource="{Binding SourceList}"  SelectionMode="Extended" StackOverflow:ListBoxMultiSelectionBehavior.TargetList="{Binding TargetList}" />
    <ListView x:Name="listTwo" Grid.Column="1" Width="50" Height="200" ItemsSource="{Binding TargetList}" />

对于你问题的第二部分,我现在没有想到的东西......对不起:/