问题描述
你好,
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));
}
推荐答案
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事件在树视图中工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!