如何使用WPF MVVM使selecteditemchanged事件在树视图中工作 [英] How to make selecteditemchanged event to work in treeview using WPF MVVM

查看:186
本文介绍了如何使用WPF MVVM使selecteditemchanged事件在树视图中工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你好,

i在我的windows应用程序中使用了treeview ..

当我每次选择/更改项目时,应该发生一些事件..

我使用wpf mvvm技术...

但是当我从树视图中选择/更改任何项目时它不会引发事件,因为我在静态资源中绑定了父网格。但我将其更改为动态的方式也..

但仍然没有工作......

请问我在代码中犯了什么错误..

plz..do回复...



我尝试过:


xaml中的


hello,
i used treeview in my windows application..
when i select/change item from it each time,should fire some event..
I used wpf mvvm technique...
But when i select/change any item from treeview it's not raising event,since i binded parent grid in static resource..but i changed it to dynamic way also..
but still not working...
please what mistake i done in code..
plz..do reply...

What I have tried:

in xaml:

 <Grid DataContext="{StaticResource vsRole}">
            <Grid.RowDefinitions>
                <RowDefinition Height="42" />
                <RowDefinition Height="50*" />
                <RowDefinition Height="200" />
            </Grid.RowDefinitions>
            <Label Content="Role" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.ColumnSpan="3" Height="40" HorizontalAlignment="Center" Margin="5" Name="label1" VerticalAlignment="Center" Width="200" FontSize="20" Grid.Row="0" />
            <DockPanel Grid.RowSpan="2" Grid.Row="1" Height="Auto" Width="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Name="dockPanel1" Background="#FFF0F0F0">
                <DataGrid Grid.Row="2" Height="Auto" DockPanel.Dock="Left" AutoGenerateColumns="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding}" Name="dgeRole" RowDetailsVisibilityMode="VisibleWhenSelected">
                    <DataGrid.Columns>
                        <DataGridTextColumn x:Name="RoleColumn" Binding="{Binding Path=Role}" Width="100" Header="Role" />
                        <DataGridComboBoxColumn ItemsSource="{Binding Source={StaticResource vsDepartment1}}" SelectedValueBinding="{Binding Path=DepartmentId}" SelectedValuePath="DepartmentId" DisplayMemberPath="Department" Width="100" Header="DepartmentId" />
                    </DataGrid.Columns>
                </DataGrid>
                <swc:DockSplitter DockPanel.Dock="Left" Width="12" Thickness="5" Background="#FF1C1779" />
                <GroupBox DockPanel.Dock="Left" Width="Auto" VerticalAlignment="Stretch" Height="Auto" HorizontalAlignment="Stretch" Name="grpBoxContent">
                    <Grid DockPanel.Dock="Right" Height="Auto" Name="gridContent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <swc:SearchTextBox Grid.Column="0" Grid.Row="0" Width="120" Margin="3,10,3,3" Name="txtSearch" HorizontalAlignment="Right" VerticalAlignment="Top"  />
                        <GroupBox DockPanel.Dock="Left" Grid.Row="1" Grid.Column="0" Width="Auto" VerticalAlignment="Top" Height="Auto" HorizontalAlignment="Stretch" Name="Group1">
                            <Grid DockPanel.Dock="Left" Height="Auto" Width="Auto" Name="Group1Grid">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                </Grid.ColumnDefinitions>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="Auto" />
                                </Grid.RowDefinitions>
                                <Label Grid.Column="0" Grid.Row="0" Margin="3,3,3,3" Content="Department" VerticalAlignment="Center" />
                                <StackPanel Grid.Column="1" Grid.Row="0" Grid.RowSpan="3" Orientation="Horizontal">
                                    <ComboBox Margin="0,0,0,0" HorizontalAlignment="Left" IsReadOnly="True"  VerticalAlignment="Top" 

                                          Name="ctrlDepartmentId" SelectedValuePath="DepartmentId" 

                                          SelectedValue="{Binding Path=DepartmentId, Mode=TwoWay, ValidatesOnExceptions=True,NotifyOnValidationError=True}" 

                                          DisplayMemberPath="Department" ItemsSource="{Binding Source={StaticResource vsDepartmentsAll}}" 

                                          Width="200" Height="Auto" />
                                    <Expander>
                                        <TreeView Height="107" Margin="-200,0,0,0" Grid.RowSpan="3" HorizontalAlignment="Left" Name="treeView1" 

                                      VerticalAlignment="Top" Width="225" SelectedValuePath="DepartmentId"

                                      ItemsSource="{Binding Source={StaticResource vsDepartment1} }"    >
                                            <TreeView.ItemTemplate>
                                                <HierarchicalDataTemplate ItemsSource="{Binding Path=eDepartment1}" >
                                                    <TextBlock Text="{Binding  Path=Department}"/>
                                                </HierarchicalDataTemplate>
                                            </TreeView.ItemTemplate>
                                            <TreeView.ItemContainerStyle>
                                                <Style TargetType="{x:Type TreeViewItem}">
                                                    <Setter Property="IsSelected" Value="{Binding IsSelected}" />
                                                </Style>
                                            </TreeView.ItemContainerStyle>
                                            <!--<i:Interaction.Triggers>
                                                <i:EventTrigger EventName="SelectedItemChanged">
                                                    <i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}" CommandParameter="argument"/>
                                                </i:EventTrigger>
                                            </i:Interaction.Triggers>-->
                                        </TreeView>
                                    </Expander>
                                </StackPanel>
                                <!--<ComboBox Grid.Column="1" Grid.Row="0" HorizontalAlignment="Left" Margin="3,3,3,3" Name="ctrlDepartmentId" SelectedValuePath="DepartmentId" SelectedValue="{Binding Path=DepartmentId, Mode=TwoWay, ValidatesOnExceptions=True,NotifyOnValidationError=True}" DisplayMemberPath="Department" ItemsSource="{Binding Source={StaticResource vsDepartment1}}" Width="120" Height="Auto" />-->
                                <Label Grid.Column="0" Grid.Row="3" Margin="3,3,3,3" Content="Role" VerticalAlignment="Center" />
                                <TextBox Grid.Column="1" Grid.Row="3" HorizontalAlignment="Left" Margin="3,3,3,3" Name="ctrlRole" Text="{Binding Path=Role, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" Width="120" Height="Auto" VerticalContentAlignment="Center" />
                                <Label Grid.Column="0" Grid.Row="4" Margin="3,3,3,3" Content="Description" VerticalAlignment="Center" />
                                <TextBox Grid.Column="1" Grid.Row="4" HorizontalAlignment="Left" Margin="3,3,3,3" Name="ctrlDescription" Text="{Binding Path=Description, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" Width="200" Height="55" TextWrapping="WrapWithOverflow" VerticalContentAlignment="Top" Grid.RowSpan="2" />
                            </Grid>
                        </GroupBox>
                    </Grid>
                </GroupBox>
            
</DockPanel>
        </Grid>



in xaml.cs file:
$ b$b


in xaml.cs file:

this.DataContext = new frmRoleVwmdl();



in ViewModel class:


in ViewModel class:

private static object _selectedItem = null;
public static object SelectedItem
{
	get { return _selectedItem; }
	private set
	{
		if (_selectedItem != value)
		{
			_selectedItem = value;
			OnSelectedItemChanged();
		}
	}
}

static  void OnSelectedItemChanged()
{
	// Raise event / do other things
	MessageBox.Show("Record Saved!!");
}

private bool _isSelected;
public bool IsSelected
{
	get { return _isSelected; }
	set
	{
		if (_isSelected != value)
		{
			_isSelected = value;
			OnPropertyChanged("IsSelected");
			if (_isSelected)
			{
				SelectedItem = this;
			}
		}
	}
}

public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
	var handler = this.PropertyChanged;
	if (handler != null)
		handler(this, new PropertyChangedEventArgs(propertyName));
}

推荐答案

Below is an update with a working example for getting the selected item for a TreeView.



I’ve separated the hierarchical collection from the data collection by using CollectionViewSource. Any changes to the collection data will be reflected in the hierarchical collection used by the view.



I have also included an example of how to sort the branches using a Converter class. It could also be done in the CollectionViewSource. If you want to use the latter, I’ll leave that exercise up to you. ;)



Here is a base class to wrap INotifyPropertyChanged:

Below is an update with a working example for getting the selected item for a TreeView.

I've separated the hierarchical collection from the data collection by using CollectionViewSource. Any changes to the collection data will be reflected in the hierarchical collection used by the view.

I have also included an example of how to sort the branches using a Converter class. It could also be done in the CollectionViewSource. If you want to use the latter, I'll leave that exercise up to you. ;)

Here is a base class to wrap INotifyPropertyChanged:
public abstract class ObservableBase : INotifyPropertyChanged
{
    public void Set<TValue>(ref TValue field, TValue newValue, [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<TValue>.Default.Equals(field, default(TValue)) || !field.Equals(newValue))
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}



The data model:


The data model:

public class EmployeeModel : ObservableBase
{
    private int id;
    public int Id
    {
        get => id;
        set => Set(ref id, value);
    }

    private string name;
    public string Name
    {
        get => name;
        set => Set(ref name, value);
    }

    private string role;
    public string Role
    {
        get => role;
        set => Set(ref role, value);
    }

    private int managerId;
    public int ManagerId
    {
        get => managerId;
        set => Set(ref managerId, value);
    }
}



A wrapper ViewModel for each data model. This handles the hierarchical collection and TreeViewItem selection :


A wrapper ViewModel for each data model. This handles the hierarchical collection and TreeViewItem selection :

public class EmployeeViewModel : ObservableBase
{
    public EmployeeModel Employee { get; set; }
    public CollectionViewSource Subordinates { get; set; }

    private bool isSelected;
    public bool IsSelected
    {
        get => isSelected;
        set => Set(ref isSelected, value);
    }
}



Now for the main view model for the view/window - binds the collection to the UI and handles the SelectedItem:


Now for the main view model for the view/window - binds the collection to the UI and handles the SelectedItem:

public class MainViewModel : ObservableBase
{
    public MainViewModel()
    {
        MockData();
    }

    private EmployeeViewModel selectedEmployee;
    public EmployeeViewModel SelectedEmployee
    {
        get => selectedEmployee;
        set => Set(ref selectedEmployee, value);
    }

    // employee list
    private ObservableCollection<EmployeeViewModel> employeesData
        = new ObservableCollection<EmployeeViewModel>();

    // employee list hierarchical view for UI
    public CollectionViewSource Employees { get; set; }

    private void MockData()
    {
        // Listen for changes to the collection
        employeesData.CollectionChanged += EmployyeeDataCollectionChanged;

        // Now add employees
        employeesData.Add(new EmployeeViewModel
        {
            Employee = new EmployeeModel
            {
                Id = 1,
                Name = "Bob"
            }
        });

        employeesData.Add(new EmployeeViewModel
        {
            Employee = new EmployeeModel
            {
                Id = 2,
                Name = "Paul",
                ManagerId = 3
            }
        });

        employeesData.Add(new EmployeeViewModel
        {
            Employee = new EmployeeModel
            {
                Id = 3,
                Name = "Mary",
                ManagerId = 1
            }
        });

        employeesData.Add(new EmployeeViewModel
        {
            Employee = new EmployeeModel
            {
                Id = 4,
                Name = "Joe",
                ManagerId = 1
            }
        });

        employeesData.Add(new EmployeeViewModel
        {
            Employee = new EmployeeModel
            {
                Id = 5,
                Name = "Jane",
                ManagerId = 2
            }
        });

        // Build hierarchical View for UI
        Employees = new CollectionViewSource { Source = employeesData };
        Employees.View.Filter =  new Predicate<object>((o)
                => (o as EmployeeViewModel)?.Employee.ManagerId == 0);

        foreach (var employee in employeesData)
        {
            employee.Subordinates = new CollectionViewSource
            { Source = employeesData };
            employee.Subordinates.View.Filter = new Predicate<object>((o)
                => (o as EmployeeViewModel)?.Employee.ManagerId
                    == employee.Employee.Id);
        }
    }

    // Listen or unlisten to employees as they're added or removed
    private void EmployyeeDataCollectionChanged(object sender,
                                                NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                for (int i = 0; i < e.NewItems.Count; i++)
                {
                    var employee = e.NewItems[i] as EmployeeViewModel;
                    employee.PropertyChanged += EmployeePropertyChanged;

                }
                break;

            case NotifyCollectionChangedAction.Remove:
                for (int i = 0; i < e.OldItems.Count; i++)
                {
                    var employee = e.NewItems[i] as EmployeeViewModel;
                    employee.PropertyChanged -= EmployeePropertyChanged;
                }
                break;
        }
    }

    // Only listen for the employee being selected
    private void EmployeePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(EmployeeViewModel.IsSelected))
        {
            SelectedEmployee = sender as EmployeeViewModel;
        }
    }
}



Note: We are listening to all the data ViewModels (EmployeeViewModel) to identify which item is being selected.



Now that the Data is ready, we can build & bind the UI:


Note: We are listening to all the data ViewModels (EmployeeViewModel) to identify which item is being selected.

Now that the Data is ready, we can build & bind the UI:

<Window

    x:Class="TreeViewSelectedItem.MainWindow"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"



    mc:Ignorable="d"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"



    xmlns:local="clr-namespace:TreeViewSelectedItem"



    Title="CodeProject  -  TREEVIEW SELECTED ITEM"

    WindowStartupLocation="CenterScreen" Height="500" Width="300">

    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <TreeView ItemsSource="{Binding Employees.View}">
            <TreeView.Resources>

                <local:TvBranchSortPropertyConverter x:Key="SortConverter"/>

                <HierarchicalDataTemplate DataType="{x:Type local:EmployeeViewModel}"

                                          ItemsSource="{Binding Subordinates.View, 
                                          Converter={StaticResource SortConverter},
                                          ConverterParameter=Employee.Name}">
                    <TextBlock Text="{Binding Employee.Name}"

                               VerticalAlignment="Center"/>
                </HierarchicalDataTemplate>

            </TreeView.Resources>

            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsSelected" Value="{Binding IsSelected}" />
                </Style>
            </TreeView.ItemContainerStyle>

        </TreeView>

        <TextBlock Text="{Binding SelectedEmployee.Employee.Name, FallbackValue=None}"

                   Grid.Row="1" Margin="10"/>

    </Grid>

</Window>



NOTE: As we are binding w ith a CollectionViewSource, we need to bind to it’s View property.



Lastly, here is the converter for custom sorting of the TreeView nodes:


NOTE: As we are binding with a CollectionViewSource, we need to bind to it's View property.

Lastly, here is the converter for custom sorting of the TreeView nodes:

public class TvBranchSortPropertyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var view = value as ListCollectionView;
        view.SortDescriptions.Add(new SortDescription(parameter.ToString(), ListSortDirection.Ascending));
        return view;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}


这篇关于如何使用WPF MVVM使selecteditemchanged事件在树视图中工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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