Мультисвязывание с множественным выбором ListView

Хорошо, у меня есть странный здесь. Я пытаюсь понять, как иметь одно представление списка, заполненное ObservableCollection, обновить другое ListView, заполненное другим ObservableCollection, на основе выбора первого списка, а затем использовать преобразователь значений, чтобы установить или снять флажок на основе комбинированного выбора с текущим элементом во втором списке. В этой части я несколько работаю с использованием множественной привязки, но часть, которая ставит меня в тупик, - это когда я проверяю или снимаю флажок с элемента во втором представлении списка, мне нужно иметь возможность захватить это событие и все отмеченные в данный момент элементы в этом списке и обновить поле базы данных на основе этого.

Я знаю, что это может не иметь большого смысла, и я изо всех сил пытаюсь сделать это более понятным, но ниже приведен xaml для двух списков и код для преобразователя. Я вижу, что когда я устанавливаю или снимаю флажок в представлении списка secodn, конвертер пытается выполнить метод ConvertBack, который взрывается, но если я устанавливаю его просто для возврата null, тогда код больше не взрывается, но флажок подсвечивается красным цветом, как будто произошла ошибка проверки.

Я даже не уверен, что множественная привязка - это то, что нужно, и я просмотрел материал просмотра списка с множественным выбором Джоша Смита, но с необходимым преобразованием я также не вижу, как это реализовать успешно.

Если у кого-то есть идеи, я был бы очень признателен. Также мои извинения, если я не очень ясно объяснил свою потребность, но я надеюсь, что с хаотичным описанием и кодом вы сможете увидеть, к чему я иду.

Заранее спасибо!

Первый 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 };
    }}

person GambitMonkey    schedule 25.08.2010    source источник


Ответы (1)


У меня может быть решение для первой части вашей проблемы, то есть «выяснить, как иметь одно представление списка, заполненное ObservableCollection, обновить другое ListView, заполненное другим ObservableCollection, на основе выбора первого списка».

Но прежде, для этой части проблемы 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);
                }

            }
        }

    }
}

Что он делает в основном, так это то, что при инициализации он извлекает элементы целевого списка и заполняет исходный список (одним способом) и подписывается на измененное событие выбора источника списка для заполнения целевого списка (другой способ). Затем целевой список обновляется при добавлении элементов в поведение.

Новый 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}" />

Что касается второй части вашей проблемы, мне сейчас ничего не приходит в голову... извините :/

person Bruno    schedule 11.04.2011