【问题标题】:Multibinding Multiselection ListView多绑定多选 ListView
【发布时间】:2011-04-03 18:12:44
【问题描述】:

好的,我这里有一个奇怪的。我想弄清楚的是如何让一个由 ObservableCollection 填充的列表视图,根据第一个列表视图的选择更新另一个由另一个 ObservableCollection 填充的 ListView,然后使用 valueconverter 根据组合的选择来选中或取消选中复选框与第二个列表视图中的当前项目。这部分我通过使用多重绑定有点工作,但让我难过的部分是当我检查或取消选中第二个列表视图中的项目时,我需要能够获取该事件以及该列表视图中所有当前检查的项目更新基于此的数据库字段。

我知道这可能没有多大意义,我正在努力使其更清晰,但下面是两个列表视图的 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 };
    }}

【问题讨论】:

    标签: wpf listview multi-select multibinding imultivalueconverter


    【解决方案1】:

    对于您问题的第一部分,我可能有一个解决方案,即“要弄清楚如何让一个由 ObservableCollection 填充的列表视图,根据第一个列表视图的选择更新另一个由另一个 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}" />
    

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

    ==> 行为:

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

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

    新的 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}" />
    

    对于您问题的第二部分,我现在没有想到什么......对不起:/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-08
      • 1970-01-01
      • 2011-07-27
      • 2011-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多