Multibinding多选的ListView [英] Multibinding Multiselection ListView

查看:157
本文介绍了Multibinding多选的ListView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好,我这里有一个奇怪的。我试图找出是如何有一个列表视图,由一个ObservableCollection填充,更新另一个ListView中,由另一个的ObservableCollection填充,基于第一ListView的选择,然后使用valueconverter基础上,选择合并,选中或取消选中复选框在第二列表视图当前项目。这部分我有些必须使用multibinding工作,但一部分有我难倒是,当我检查或第二列表视图取消一个项目,我需要能够抓住这个事件,所有在该列表视图更新一个当前选中的项基于数据库字段。

我知道这可能不会让有很大的意义,我挣扎如何使它更清晰,但低于是两个列表视图和code转换器的XAML。我可以看到,当我选中或取消选中该转换器试图做ConvertBack方法,炸毁secodn列表视图一个盒子,但如果我将它设置为只返回null,则code不再吹了起来,但复选框以红色突出显示像一个验证错误发生。

我甚至不相信multibinding是去这里的路,我已经看过约什 - 史密斯的多选列表视图的东西,但有必要转换我看不出如何实现成功要么。

如果任何人有任何想法,我将不胜AP preciate它。也是我道歉,如果我没有解释我的需要很清楚,但我用混沌描述和$ C $希望C,可那种看到我用它去。

在此先感谢!

第一的ListView哺养第二个

 <网格和GT;
                < ListView的X:名称=listRule的Horizo​​ntalAlignment =拉伸VerticalAlignment =拉伸保证金=3,3,3,3的ItemsSource ={结合RuleListing}EXTS:Selected.Command ={结合RuleSelectedCommand} 的SelectedIndex =0>
                    < ListView.ItemTemplate>
                        <&DataTemplate的GT;
                            < StackPanel的方向=横向>
                                < TextBlock的文本={绑定路径=显示名称}工具提示={绑定路径=前pression}粗细=大胆/>
                                < TextBlock的文本=(/>
                                < TextBlock的文本={绑定描述}fontstyle的=斜体/>
                                < TextBlock的文本=)/>
                            < / StackPanel的>
                        < / DataTemplate中>
                    < /ListView.ItemTemplate>
                < /&的ListView GT;
            < /网格和GT;

二的ListView与转换器和multibinding

 <电网的Horizo​​ntalAlignment =拉伸>
                < Grid.Resources>
                    <转换器:RuleToRoleBooleanConverter X:键='RuleRoleConverter/>
                    <的DataTemplate X:键=RoleTemplate>
                        <电网的Horizo​​ntalAlignment =拉伸>
                            < Grid.ColumnDefinitions>
                                < ColumnDefinition WIDTH =1 *了minWidth =200/>
                                < ColumnDefinition宽度=20/>
                            < /Grid.ColumnDefinitions>
                            < TextBlock的文本={结合ROLENAME}的Horizo​​ntalAlignment =左保证金=3,0,0,0Grid.Column =0/>
                            <复选框的Horizo​​ntalAlignment =右保证金=0,0,3,0Grid.Column =1>
                                < CheckBox.IsChecked>
                                    < MultiBinding转换器={StaticResource的RuleRoleConverter}>
                                        <绑定的ElementName =listRulePATH =的SelectedItem/>
                                        <绑定路径=角色名/>
                                    < / MultiBinding>
                                < /CheckBox.IsChecked>
                            < /复选框>
                        < /网格和GT;
                    < / DataTemplate中>
                < /Grid.Resources>
                < ListView控件名称=listRoles的ItemsSource ={结合RoleListing}的Horizo​​ntalAlignment =拉伸VerticalAlignment =弹力
                          的SelectionMode =多的ItemTemplate ={StaticResource的的ResourceKey = RoleTemplate}>
                    < ListView.ItemContainerStyle>
                        <风格的TargetType ={X:输入一个ListBoxItem}>
                            < setter属性=IsSelectedVALUE ={结合模式=双向,路径= IsRoleSelected}/>
                        < /样式和GT;
                    < /ListView.ItemContainerStyle>
                < /&的ListView GT;
            < /网格和GT;

值转换

 公共类RuleToRoleBooleanConverter:IMultiValueConverter
{
    公共对象转换(对象[]值类型TARGETTYPE,对象参数,System.Globalization.CultureInfo文化)
    {
        如果(值[0] =空&放大器;!&放大器;!值[1] =空)
        {
            字符串前pression =((EliteExtenderRule)值[0])防爆pression。
            串角色=值[1]的ToString();            如果(如pression.Contains(R:*)||前pression.Contains(R:+角色))
            {
                返回true;
            }
        }
        返回false;
    }    公用对象[] ConvertBack(对象的值,类型[] targetTypes,对象参数,System.Globalization.CultureInfo文化)
    {
        返回null; //新的对象[] {(布尔)值null};
    }}


解决方案

我可能对您的问题,即要弄清楚是第一部分的解决方案如何有一个列表视图,由一个ObservableCollection填充,更新其他的ListView由另一个的ObservableCollection填充,基于第一列表视图的选择

但在此之前,对于这个问题确实DIT只绑定目标的ListView上他挑选源的ListView项属性将满足你的需要,这部分?

 < ListView的X:名称=那么listOneGrid.Column =0WIDTH =50HEIGHT =200的ItemsSource ={结合SOURCELIST}的SelectionMode =扩展/>
    < ListView的X:名称=listTwoGrid.Column =1WIDTH =50HEIGHT =200的ItemsSource ={绑定的ElementName =那么listOne,路径= SelectedItems}/>

如果不是这样,我对最近的项目有类似的问题,我想出了应用于源列表中的行为。

说,你有这样的视图的DataContext的视图模型:

 公开的ObservableCollection< BusinessAdapter> SOURCELIST {搞定;私人集; }
    公众的ObservableCollection< BusinessAdapter> TARGETLIST {搞定;私人集; }
    公共窗口1()
    {
        的InitializeComponent();
        的DataContext =这一点;
        SOURCELIST =新的ObservableCollection< BusinessAdapter>();
        TARGETLIST =新的ObservableCollection< BusinessAdapter>();        的for(int i = 0; I< 50;我++)
        {
            SourceList.Add(新BusinessAdapter {BusinessProperty =blabla_+ I});
        }
    }

和在你看来,你有这样的事情:

 < ListView的X:名称=那么listOneGrid.Column =0WIDTH =50HEIGHT =200的ItemsSource ={结合SOURCELIST}的SelectionMode =扩展/>
    < ListView的X:名称=listTwoGrid.Column =1WIDTH =50HEIGHT =200的ItemsSource ={结合TARGETLIST}/>

然后就可以在源列表框附上这个行为和目标视图模型源传递给它。

==>的行为:

 公共类ListBoxMultiSelectionBehavior
{
    公共静态的IList< BusinessAdapter> GetTargetList(DependencyObject的OBJ)
    {
        返回(IList的< BusinessAdapter>)obj.GetValue(TargetListProperty);
    }    公共静态无效SetTargetList(OBJ的DependencyObject,IEnumerable的< BusinessAdapter>值)
    {
        obj.SetValue(TargetListProperty,值);
    }    //使用的DependencyProperty作为后备存储适配器。这使得动画造型,装订等..
    公共静态只读的DependencyProperty TargetListProperty =
        DependencyProperty.RegisterAttached(TARGETLIST的typeof(IList的< BusinessAdapter>)的typeof(ListBoxMultiSelectionBehavior),新UIPropertyMetadata(NULL,OnListChanged));    ///<总结>
    ///型号列表改变回调
    ///< /总结>
    ///< PARAM NAME =D>< /参数>
    ///< PARAM NAME =E>< /参数>
    私有静态无效OnListChanged(DependencyObject的D,DependencyPropertyChangedEventArgs E)
    {
        VAR mSelector = D为ListView的;        如果(mSelector!= NULL)
        {
            mSelector.SelectionChanged - = MSelectorSelectionChanged;            //多或推广的选择模式是强制性的
            如果(mSelector.SelectionMode == SelectionMode.Single)
            {
                返回;
            }
            mSelector.SelectedItems.Clear();            //绑定=>模型查看
            //获取模型的列表
            VAR一个= GetTargetList(D);
            如果(一个!= NULL)
            {
                //对于列表中的每个模型
                的foreach(在一个变种BA)
                {
                    //列表框中的项目集合
                    的foreach(在mSelector.Items VAR项)
                    {
                        //找到对应关系,如发现
                        如果(((BusinessAdapter)项).BusinessProperty == ba.BusinessProperty)
                        {
                            //添加物品到所选项目
                            mSelector.SelectedItems.Add(项目);                        }
                    }                }
            }            //绑定=>查看模型
            //订阅变化,选择列表框中
            mSelector.SelectionChanged + = MSelectorSelectionChanged;
        }
    }    静态无效MSelectorSelectionChanged(对象发件人,SelectionChangedEventArgs E)
    {
        如果(E!= NULL)
        {
            VAR适配器= GetTargetList(发件人为DependencyObject的);
            无功名单=适配器; //复制            如果(e.RemovedItems.Count大于0 / *&放大器;&放大器;!e.RemovedItems.Count = list.Count * /)
            {
                的foreach(VAR学士学位e.RemovedItems.Cast< BusinessAdapter>())
                {
                    list.Remove(BA);
                }
            }
            如果(e.AddedItems.Count大于0)
            {
                的foreach(VAR学士学位e.AddedItems.Cast< BusinessAdapter>())
                {
                    list.Add(BA);
                }            }
        }    }
}

它的作用主要是初始化时,他retreive目标列表项和填充源列表(单程),并订阅列表框中源选择更改事件来填充目标列表(其他方式)。然后,当项目在bahavior添加更新的目标列表框中武功。

新的XAML看起来就像是:

 < ListView的X:名称=那么listOneGrid.Column =0WIDTH =50HEIGHT =200的ItemsSource ={结合SOURCELIST}的SelectionMode =扩展计算器:ListBoxMultiSelectionBehavior.TargetList ={结合TARGETLIST}/>
    < ListView的X:名称=listTwoGrid.Column =1WIDTH =50HEIGHT =200的ItemsSource ={结合TARGETLIST}/>

有关您的问题的第二部分,我没有的东西,我的脑海里来,现在...对不起:/

OK I have a weird one here. What I am trying to figure out is how to have one listview, populated by an ObservableCollection, update another ListView, populated by another ObservableCollection, based on the first listview's selection and then use a valueconverter to check or uncheck a checkbox based on the selection combined with the current item in the second listview. This part I somewhat have working by using multibinding, but the part that has me stumped is when I check or uncheck an item in the second listview I need to be able to grab that event and all the currently checked items in that listview an update a database field based on that.

I know this may not make a lot of sense and I'm struggling on how to make it clearer, but below is the xaml for the two listviews and the code for the converter. I can see that when I check or uncheck a box in the secodn listview that the converter tries to do the ConvertBack method which blows up, but if I set it to just return null then the code no longer blows up, but the checkbox is highlighted in red like a validation error has occured.

I am not even sure that multibinding is the way to go here and I have looked at Josh Smith's multiselection listview stuff, but with the conversion needed I can not see how to implement that successfully either.

If anyone has any ideas I would greatly appreciate it. Also my apologies if I didn't explain my need very clearly, but I am hoping with the chaotic description and the code you can kind of see where I am going with it.

Thanks in advance!

First ListView that feeds the second one

<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>

Second ListView with converter and multibinding

<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>

Value Converter

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

解决方案

I might have a solution for the first part of your problem, i.e "to figure out is how to have one listview, populated by an ObservableCollection, update another ListView, populated by another ObservableCollection, based on the first listview's selection".

But before, for this part of the problem dit does just bind the target ListView on he selected items property of the source ListView would satisfy your needs ?

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

If not, I had a similar problem on recent project and I came up with a behavior applied to source list.

Saying that you have a view model like that as the DataContext of your view :

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

And in your view you have something like that :

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

Then you can attach this behavior on the source listbox and pass the target view model source to it.

==> the behavior:

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

            }
        }

    }
}

What it does basically is that when initializing, he retreive the target list items and populates the source list (one way), and subscribe to list box source selection changed event to populate target list (the other way). Then, the target listbox gest updated when items are added in the bahavior.

The new XAML looks just like that :

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

For the second part of your problem, I don't have something that come to my mind right now... sorry :/

这篇关于Multibinding多选的ListView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆