Двухстороннее связывание не происходит между представлением дерева и комбинированным окном в WPF - PullRequest
0 голосов
/ 25 января 2020

У меня есть пользовательский элемент управления, который имеет древовидное представление и комбинированное окно. Древовидное представление имеет привязку к источнику элементов с использованием класса модели представления. Исходный элемент элемента комбинированного окна передается через код позади. Я связал путь Selected Id как двухстороннюю привязку между Поле со списком и представление дерева. Теперь, когда приложение запускается, когда элемент управления заполняется и отображается данные, и когда я выбираю какой-либо элемент в окне Treeview..com, выбирается элемент. Но когда я выбираю любой элемент из поля со списком, соответствующий элемент не быть выбранным в древовидном списке .. хотя это двустороннее связывание .. пожалуйста, помогите мне решить эту проблему .. моя цель состоит в том, чтобы когда пользователь выбирал какой-либо элемент в поле со списком, тот же элемент должен быть выбран в древовидном представлении Мой пользовательский элемент управления:

  <UserControl x:Class="gsktid.MyUserControl.TreeView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
             xmlns:local="clr-namespace:gsktid.MyUserControl"
          xmlns:local1="clr-namespace:gsktid.ViewModel" 
          xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">

<StackPanel>
    <TextBlock HorizontalAlignment="Left" Margin="20,0,0,0" Width="Auto" Text="Search:" TextWrapping="Wrap" VerticalAlignment="Center" FontFamily="Arial"/>
    <ComboBox x:Name="cmbSerchBox" Width="120" DisplayMemberPath = "FirstName"  SelectedIndex = "-1"   SelectedValuePath = "Id"  SelectedValue="{Binding  Path=SelectedId,Mode=TwoWay}"   HorizontalAlignment="Left" Margin="8"  >
  Path=SelectedId}"/>
        </i:EventTrigger>-->
    </ComboBox>
    <TreeView Height="500" x:Name="tvMain" ItemsSource="{Binding Root}" BorderThickness="0" ScrollViewer.VerticalScrollBarVisibility="Visible">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                <StackPanel Orientation="Horizontal">
                    <Image Source="{Binding ImagePath}" MaxHeight="25" MaxWidth="25"/>
                    <TextBlock VerticalAlignment="Center">            
                    <TextBlock.Text>
                        <MultiBinding StringFormat=" {0} ">
                            <Binding Path="FirstName"/>

                        </MultiBinding>
                    </TextBlock.Text>
                    </TextBlock>
                    <StackPanel.Style>
                        <Style>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsSelected}" Value="true">
                                    <Setter Property="StackPanel.Background" Value="LightBlue"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </StackPanel.Style>
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsExpanded" Value="True"/>
                <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
            </Style>
        </TreeView.ItemContainerStyle>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectedItemChanged">
                <i:InvokeCommandAction Command="{Binding SelectedCommand}" 
                                       CommandParameter="{Binding ElementName=tvMain, Path=SelectedItem}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TreeView>


</StackPanel>

Мой код этого пользовательского элемента управления:

using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using gsktid.ViewModel;
using gsktid.Model;
namespace gsktid.MyUserControl
 {

     public partial class TreeView : UserControl
{
    public TreeView()
    {
        InitializeComponent();
        this.DataContext = OrgTreeViewModel.Instance();
        cmbSerchBox.ItemsSource = OrgChartManager.Instance().GetChildrenSerch();

    }
}      

}


Здесь Combobox представляет собой набор данных с данными источник элемента в коде позади. Но источник элемента Treeview заполняется набором данных модели представления. Оба набора данных имеют один общий ключ. Таким образом, когда я выбираю элемент в древовидном представлении, выбирается соответствующий элемент в поле со списком. Но наоборот не происходит. Когда я выбираю один элемент в комбинированном окне, выбирается соответствующий элемент в виде дерева. хотя это двустороннее связывание

То, что я пробовал:

Я пытался связать поле со списком и древовидное представление ... как двухстороннее связывание ... но оно работает в одном направлении. когда когда я делаю изменения выбора в Treeview, это отражается в поле со списком ... но когда я делаю изменения выбора в поле со списком, это не влияет на представление дерева

1 Ответ

1 голос
/ 26 января 2020

Я не это имел ввиду! Я имел в виду, что нам нужно что-то, где мы можем создать новый проект приложения Visual Studio WPF, вставить код, запустить и увидеть проблему. Вы не можете сделать это с помощью кода в текущем вопросе, верно?

Я понимаю, что с оригинальным кодом это не так просто. Тем не менее, я на самом деле создал версию ранее сегодня. Это не идеально, потому что я не вижу весь ваш код, поэтому мне пришлось угадывать в нескольких местах.

Этот код находится в самом низу этого ответа. Это то, что мы подразумеваем под минимальным воспроизводимым примером.

Если вы создадите новый проект и вставите код внизу в файлы MainWindow XAML и C#, он покажет пользовательский элемент управления с полем со списком и древовидная структура, и нажатие в любом из них выберет правильный элемент в другом. Он основан на вашем исходном коде, и XAML практически идентичен. На самом деле вам также нужно установить пакет Microsoft.Xaml.Behaviors.Wpf NuGet, поскольку ваш XAML использует поведения.

Проблема в исходном вопросе заключалась в том, что свойство IsSelected в соответствующем элементе OrgElementViewModel в TreeView не был установлен, когда ComboBox был изменен. Это произошло потому, что единственное свойство, которое обновлялось при изменении ComboBox, было SelectedId.

Решением ниже является использование установщика SelectedId для обновления свойства IsSelected и для полного завершения элемента Selected в OrgTreeViewModel, если вы все еще со мной. Ниже приведен важный код, который заменяет существующий код SelectedId в OrgTreeViewModel:

    public string SelectedId
    {
        get { return selectedId; }
        set
        {
            selectedId = value;
            OrgElementViewModel orgElementViewModel = FindById(selectedId);
            if (orgElementViewModel != null) this.Selected = orgElementViewModel;
            OnPropertyChanged("SelectedId");
        }
    }

Обратите внимание, что здесь есть метод FindById, который должен принять selectedId и найти соответствующий элемент OrgElementViewModel в дереве. Я не вижу метода, который делает это, и его немного сложно написать без вашего рабочего кода: почти наверняка есть лучший способ сделать это, чем рекурсивный поиск в решении ниже. Также я не мог понять, как комбо было заполнено, поэтому создал свой собственный список и связал его с ним.

Дайте мне знать, если это поможет!

Полный рабочий пример: XAML

<Window x:Class="ComboTreeBinding.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:i="http://schemas.microsoft.com/xaml/behaviors" 
        xmlns:local="clr-namespace:ComboTreeBinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel Height="200" Width="200">
        <TextBlock HorizontalAlignment="Left" Margin="20,0,0,0" Width="Auto" Text="Search:" TextWrapping="Wrap" VerticalAlignment="Center" FontFamily="Arial"/>
        <ComboBox x:Name="cmbSerchBox" Width="120" ItemsSource="{Binding List}" DisplayMemberPath = "FirstName"  SelectedIndex = "-1"   SelectedValuePath = "Id"  SelectedValue="{Binding  Path=SelectedId,Mode=TwoWay}"   HorizontalAlignment="Left" Margin="8"  >
        </ComboBox>
        <TreeView Height="150" x:Name="tvMain" ItemsSource="{Binding Root}" BorderThickness="0" ScrollViewer.VerticalScrollBarVisibility="Visible">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <StackPanel Orientation="Horizontal">
                         <TextBlock VerticalAlignment="Center">
                            <TextBlock.Text>
                                <MultiBinding StringFormat=" {0} ">
                                    <Binding Path="FirstName"/>

                                </MultiBinding>
                            </TextBlock.Text>
                        </TextBlock>
                        <StackPanel.Style>
                            <Style>
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding IsSelected}" Value="true">
                                        <Setter Property="StackPanel.Background" Value="LightBlue"/>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </StackPanel.Style>
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded" Value="True"/>
                    <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
                </Style>
            </TreeView.ItemContainerStyle>
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectedItemChanged">
                    <i:InvokeCommandAction Command="{Binding SelectedCommand}" 
                                       CommandParameter="{Binding ElementName=tvMain, Path=SelectedItem}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </TreeView>


    </StackPanel>
</Window>

Полный рабочий пример, C#:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;

namespace ComboTreeBinding
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new OrgTreeViewModel();
        }
    }

    public class OrgTreeViewModel: INotifyPropertyChanged
    {
        public OrgTreeViewModel()
        {
            this.CreateDataList();
        }
        public ObservableCollection<OrgElementViewModel> List { get; set; }
        private List<OrgElementViewModel> root;
        public List<OrgElementViewModel> Root
        {
            get
            {
                if (root == null) root = new List<OrgElementViewModel>{List[0]};
                return root;
            }
        }

        public void CreateDataList()
        {
            ObservableCollection<OrgElementViewModel> list = new ObservableCollection<OrgElementViewModel>();
            list.Add(new OrgElementViewModel("1", "AAAAA", "0"));
            list.Add(new OrgElementViewModel("2", "BBBBB", "1"));
            list.Add(new OrgElementViewModel("3", "CCCCC", "1"));
            list.Add(new OrgElementViewModel("7", "DDDDD", "2"));
            this.List = list;
            foreach (OrgElementViewModel item in list) SetChildren(item);
        }

        private void SetChildren(OrgElementViewModel Parent)
        {
            foreach (OrgElementViewModel listItem in List)
            {
                if (listItem.ParentId == Parent.Id) Parent.Children.Add(listItem);
            }
        }

        private ICommand selectedCommand;
        public ICommand SelectedCommand
        {
            get
            {
                if (selectedCommand == null)
                {
                    selectedCommand = new CommandBase(i => this.SetSelected(i), null);
                }

                return selectedCommand;
            }
        }

        private void SetSelected(object orgElement)
        {
            this.Selected = orgElement as OrgElementViewModel;
            SelectedId = this.Selected.Id;

            OnPropertyChanged("SelectedId");

        }

        private OrgElementViewModel selected;
        public OrgElementViewModel Selected
        {
            get { return selected; }
            set
            {
                selected = value;
                selected.IsSelected = true;
                //ShowChildrenLevel();  //show only the levels chosen by the user
                OnPropertyChanged("Selected");
            }
        }

        private string selectedId;
        public string SelectedId
        {
            get { return selectedId; }
            set
            {
                selectedId = value;
                OrgElementViewModel orgElementViewModel = FindById(selectedId);
                if (orgElementViewModel != null) this.Selected = orgElementViewModel;
                OnPropertyChanged("SelectedId");
            }
        }

        private OrgElementViewModel FindById(string ID)
        {
            foreach(OrgElementViewModel item in this.List)
            {
                if (item.Id == ID) return item;
            }
            return null;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class OrgElementViewModel: INotifyPropertyChanged
    {
        public string Id { get; set; }
        public string FirstName { get; set; }
        public string ParentId { get; set; }
        private bool isSelected;
        public ObservableCollection<OrgElementViewModel> Children { get; set; }
        public OrgElementViewModel(string Id, string FirstName, string ParentId)
        {
            this.Id = Id;
            this.FirstName = FirstName;
            this.ParentId = ParentId;
            this.Children = new ObservableCollection<OrgElementViewModel>();
        }

        public bool IsSelected
        {
            get { return isSelected; }
            set
            {
                isSelected = value;
                OnPropertyChanged("IsSelected");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class CommandBase : ICommand
    {
        private Action<object> commandDelegate;
        private object commandParameter;

        public CommandBase(Action<object> commandDelegate, object commandParameter)
        {
            this.commandDelegate = commandDelegate;
            this.commandParameter = commandParameter;
        }

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter) => true;
        public void Execute(object parameter)
        {
            commandDelegate?.Invoke(parameter);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...