I want to do the following:
I have a combobox that is bound to the ComputerNames property. Once I select a new computer
the XAML will do everything for me and the new SelectedComputer is set.
This is the point I'm not sure how to continue. So every time a new computer is selected I want the
CustomerNames property to change to (and subsequently the CustomerNames combobox). The CustomerNames
are dependent on the SelectedComputer property. How and where do I put the logic to give the CustomerComboBox
the items it needs?
MainWindow.xaml.cs
public partial class MainWindow : Window
{
MainViewModel _main = new MainViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _main;
}
}
XAML:
<Window.Resources>
<vm:MainViewModel x:Key="viewModel" />
</Window.Resources>
<Border Margin="10">
<Grid>
<!-- Comboboxes -->
<GroupBox Header="Computer" Grid.Column="0" Grid.ColumnSpan="4" Margin="0 0 4 4">
<ComboBox ItemsSource="{Binding ComputerNames}"
SelectedItem="{Binding SelectedComputer}"
IsSynchronizedWithCurrentItem="True"
SelectedIndex="0"/>
</GroupBox>
<GroupBox Header="Customer" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4" Margin="0 0 4 4" >
<ComboBox ItemsSource="{Binding CustomerNames}"
SelectedItem="{Binding SelectedCustomer}"
IsSynchronizedWithCurrentItem="True"
SelectedIndex="0"/>
</GroupBox>
<!-- Main list -->
<DataGrid x:Name="dataGrid" Grid.Row="2" Grid.ColumnSpan="8"
ItemsSource="{Binding Path=Services, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
AutoGenerateColumns="False"
IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Width="*" Binding="{Binding DisplayName}"/>
<DataGridTextColumn Header="Status" Width="*" Binding="{Binding Status}" />
<DataGridTextColumn Header="Machine Name" Width="*" Binding="{Binding MachineName}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Border>
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
#region INotify methods
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
#endregion
/*---------------------------- C U S T O M E R S -----------------------------*/
#region Customer Properties
private string _selectedCustomer;
private ObservableCollection<string> _customerNames;
public string SelectedCustomer
{
get => _selectedCustomer;
set
{
SetField(ref _selectedCustomer, value);
Services = Utils.UpdatedServices(SelectedComputer, SelectedCustomer);
}
}
public ObservableCollection<string> CustomerNames
{
get => _customerNames;
set
{
SetField(ref _customerNames, value);
Services = Utils.UpdatedServices(SelectedComputer, SelectedCustomer);
}
}
#endregion
/*---------------------------- S E R V I C E S -----------------------------*/
#region Services Properties
private ObservableCollection<ServiceController> _services;
private ObservableCollection<ServiceController> _selectedServices;
public ObservableCollection<ServiceController> SelectedServices
{
get => _selectedServices;
set => SetField(ref _selectedServices, value);
}
public ObservableCollection<ServiceController> Services
{
get => _services;
set => SetField(ref _services, value);
}
#endregion
/*---------------------------- C O M P U T E R S -----------------------------*/
#region Computer Properties
private string _selectedComputer;
private ObservableCollection<string> _computerNames;
public string SelectedComputer
{
get => _selectedComputer;
set
{
SetField(ref _selectedComputer, value);
CustomerNames = Utils.UpdatedCustomerNames(SelectedComputer);
}
}
public ObservableCollection<string> ComputerNames
{
get => _computerNames;
set => SetField(ref _computerNames, value);
}
#endregion
/*---------------------------- C O M M A N D S -----------------------------*/
#region Commands
public StartServiceCommand StartServiceCommand { get; set; }
public void StartService(ObservableCollection<ServiceController> serviceControllers)
{
try
{
foreach(var service in serviceControllers)
if (service.Status != ServiceControllerStatus.Running)
service.Start();
RefreshServices();
}
catch (Exception ex) { }
}
public StopServiceCommand StopServiceCommand { get; set; }
public void StopService(ObservableCollection<ServiceController> serviceControllers)
{
try
{
foreach (var service in serviceControllers)
if (service.Status != ServiceControllerStatus.Stopped &&
service.Status != ServiceControllerStatus.StopPending)
service.Stop();
RefreshServices();
}
catch (Exception ex) { }
}
public RefreshServicesCommand RefreshServicesCommand { get; set; }
public void RefreshServices()
{
Services = Utils.UpdatedServices(SelectedComputer, SelectedCustomer);
}
#endregion
public MainViewModel()
{
#region Initialize
_services = new ObservableCollection<ServiceController>();
_computerNames = new ObservableCollection<string>();
_customerNames = new ObservableCollection<string>();
_selectedComputer = _customerNames.FirstOrDefault();
_selectedServices = new ObservableCollection<ServiceController>();
ComputerNames = new ObservableCollection<string> { Environment.MachineName, Environment.MachineName };
StartServiceCommand = new StartServiceCommand(this);
StopServiceCommand = new StopServiceCommand(this);
RefreshServicesCommand = new RefreshServicesCommand(this);
#endregion
}
}
So every time a new computer is selected I want the CustomerNames property to change to (and subsequently the CustomerNames combobox). The CustomerNames are dependent on the SelectedComputer property. How and where do I put the logic to give the CustomerComboBox the items it needs?
For example in the setter of the SelectedComputer property in the view model:
public string SelectedComputer
{
get => _selectedComputer;
set
{
SetField(ref _selectedComputer, value);
//set the CustomerNames property here...
CustomerNames = ?
}
}
It's in the view model that you should implement your application logic, like for example what happens when you click on a button or select an item in a ComboBox.
Related
I'm trying to filter a list based on what a user types into a textbox. However, nothing is happening as the user types into the box. As I've been debugging, I've placed breakpoints on the setter for this binding, but they don't trigger.
TextBox definition:
<TextBox HorizontalAlignment="Center" Text="{Binding TESTSerialNumbSearchTerm, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" ToolTip="Filter Part Number" Width="180"/>
ViewModel Binding:
public String TESTSerialNumbSearchTerm
{
get
{
return serialNumbSearchTerm;
}
set
{
if (serialNumbSearchTerm != value)
{
serialNumbSearchTerm = value;
VisibleProfiles = FilterList(VisibleProfiles, Tuple.Create("serialNumber", value));
OnPropertyChanged(nameof(VisibleProfiles));
OnPropertyChanged(nameof(TESTSerialNumbSearchTerm));
}
}
}
Grid definition, with ItemSource:
<DataGrid MaxHeight="400" Grid.Row="0" ItemsSource="{Binding VisibleProfiles}" SelectedItem="{Binding SelectedProfile}" SelectionMode="Single" IsReadOnly="True" AutoGenerateColumns="False" VerticalScrollBarVisibility="Visible">
FilterList Method:
public List<DongleProfile> FilterList(List<DongleProfile> inputList, Tuple<string, string> filter)
{
List<DongleProfile> newList = new List<DongleProfile>();
foreach (DongleProfile item in inputList)
{
switch (filter.Item1)
{
case "serialNumber":
if (item.SerialNumberPrefix.Contains(filter.Item2))
{
newList.Add(item);
}
break;
// Similar cases
}
}
return newList;
}
If the TextBox is located in a DataGrid, you could use a RelativeSource to bind to a property of the view model:
<TextBox HorizontalAlignment="Center"
Text="{Binding DataContext.TESTSerialNumbSearchTerm, UpdateSourceTrigger=PropertyChanged,
RelativeSource={RelativeSource AncestorType=DataGrid}}"
ToolTip="Filter Part Number" Width="180"/>
Try the following idea:
Public field for filtering textbox
public string md_FilterString
{
get { return _FilterString; }
set
{
if (_FilterString != value)
{
_FilterString = value;
mf_MakeView();
OnPropertyChanged("md_FilterString");
}
}
}
Public field for datagrid binding:
public ICollectionView md_LogEntriesStoreView { get; private set; }
XAML:
..
<TextBox Grid.Column="1"
Text="{Binding md_FilterString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Height="22"
HorizontalAlignment="Stretch"
Margin="0,0,0,0"
Name="textBoxFilter"
VerticalAlignment="Center"/>
..
<DataGrid ItemsSource="{Binding md_LogEntriesStoreView, UpdateSourceTrigger=PropertyChanged}"
..
</DataGrid>
mf_MakeView func configures the composition of the collection md_LogEntriesStoreView:
private void mf_MakeView()
{
if (d_Items== null) return;
md_LogEntriesStoreView = CollectionViewSource.GetDefaultView(d_Items);
md_LogEntriesStoreView.Filter = mf_UserFilter;
OnPropertyChanged("md_LogEntriesStoreView");
}
Where d_Items - are directly the elements of your observable collection that will be displayed in the control datagrid
The filtering function (mf_UserFilter) is presented in a general way for an object containing text fields. You can replace it for optimization purposes with your own version, adapted to your goals:
private bool mf_UserFilter(object item)
{
string s = md_FilterString;
if (String.IsNullOrWhiteSpace(s))
return true;
else
{
var srcT = item.GetType();
foreach (var f in srcT.GetFields())
{
string str = f.GetValue(item) as string;
if (String.IsNullOrWhiteSpace(str)) continue;
bool b = str.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0;
if (b) return true;
}
foreach (var f in srcT.GetProperties())
{
string str = f.GetValue(item, null) as string;
if (String.IsNullOrWhiteSpace(str)) continue;
bool b = str.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0;
if (b) return true;
}
return false;
}
}
UPDATE:
Full text:
Code part:
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindow_ModelView();
}
}
public class MainWindow_ModelView : NotifyBase
{
private string _FilterString = String.Empty;
public ObservableCollection<ItemClass> d_Items { get; set; }
public ICollectionView md_LogEntriesStoreView { get; private set; }
public string md_FilterString
{
get { return _FilterString; }
set
{
if (_FilterString != value)
{
_FilterString = value;
mf_MakeView();
OnPropertyChanged("md_FilterString");
}
}
}
public MainWindow_ModelView()
{
d_Items = new ObservableCollection<ItemClass>() { new ItemClass() { d_Text1 = "Item1Text1", d_Text2 = "Item1Text2" },
new ItemClass() { d_Text1 = "Item2Text1", d_Text2 = "Item2Text2" },
new ItemClass() { d_Text1 = "Item3Text1", d_Text2 = "Item3Text2" } };
md_LogEntriesStoreView = CollectionViewSource.GetDefaultView(d_Items);
}
private void mf_MakeView()
{
if (d_Items == null) return;
md_LogEntriesStoreView = CollectionViewSource.GetDefaultView(d_Items);
md_LogEntriesStoreView.Filter = mf_UserFilter;
OnPropertyChanged("md_LogEntriesStoreView");
}
private bool mf_UserFilter(object item)
{
string s = _FilterString;
if (String.IsNullOrWhiteSpace(s))
return true;
else
{
var srcT = item.GetType();
foreach (var f in srcT.GetFields())
{
string str = f.GetValue(item) as string;
if (String.IsNullOrWhiteSpace(str)) continue;
bool b = str.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0;
if (b) return true;
}
foreach (var f in srcT.GetProperties())
{
string str = f.GetValue(item, null) as string;
if (String.IsNullOrWhiteSpace(str)) continue;
bool b = str.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0;
if (b) return true;
}
return false;
}
}
}
public class ItemClass : NotifyBase
{
public string d_Text1 { get; set; }
public string d_Text2 { get; set; }
}
public class NotifyBase : INotifyPropertyChanged
{
Guid id = Guid.NewGuid();
[Browsable(false)]
[System.Xml.Serialization.XmlAttribute("ID")]
public Guid ID
{
get { return id; }
set
{
if (id != value)
{
id = value;
OnPropertyChanged("ID");
}
}
}
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
XAML part:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Height="23"
Text="{Binding md_FilterString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch"
Name="textBox1"
Margin="2"
VerticalAlignment="Top"/>
<DataGrid ItemsSource="{Binding md_LogEntriesStoreView}"
AutoGenerateColumns="False"
Grid.Row="1"
Margin="2"
HorizontalAlignment="Stretch"
Name="dataGrid1"
VerticalAlignment="Stretch">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path = d_Text1}"
Width="Auto"
IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Path = d_Text2}"
Width="*"
IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Result:
Im facing an issue with WPF DataGrid Checkboxes C#.
Im not finding a way to select all cell template checkboxes when the header template checkbox is selected. in viewmodel its working fine. it get select all but in view it no showing any selected checkbox sign/mark on checked header checkbox.The problem I'm stuck with is related to checkbox in DataGrid(WPF)
click this link I want to do same like this
My XAML code :
<DataGrid x:Name="DgLines" ItemsSource="{Binding OpcUaEndpoints}"
MouseDoubleClick="DgLines_MouseDoubleClick" SelectionMode="Extended"
DataContext="{Binding}" IsReadOnly="True" Grid.ColumnSpan="5">
<DataGrid.Columns>
<DataGridTemplateColumn
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Name="ckbSelectedAll" Checked="ckbSelectedAll_Checked" Unchecked="ckbSelectedAll_Unchecked"
IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"></CheckBox>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="cbkSelect" Checked="cbkSelect_Checked" Unchecked="cbkSelect_Unchecked"
IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Width="200" Header="Id" Binding="{Binding Id }" />-->
<DataGridTextColumn Width="200" Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Width="500" Header="Description" Binding="{Binding Description}"/>
<DataGridTextColumn Width="500" Header="Lines" Binding="{Binding Endpoint}"/>
</DataGrid.Columns>
</DataGrid>
ViewModelCode:
private void ckbSelectedAll_Unchecked(object sender, RoutedEventArgs e)
{
// this.DgLines.UnselectAll();
foreach (AddLinesViewModel c in DgLines.ItemsSource)
{
c.IsSelected = false;
}
}
private static OpcUaEndpointsListViewModel _instance;
private static readonly object Padlock = new object();
private ICommand _addCommand;
private ICommand _uncheckCommand;
private ICommand _searchcommand;
private ObservableCollection<AddOpcUaEndpointsViewModel> _endpoint;
public string _charNameFromTB;
public OpcUaEndpointsListViewModel()
{
BindDataGrid();
}
public static OpcUaEndpointsListViewModel Instance
{
get
{
lock (Padlock)
{
return _instance ?? (_instance = new OpcUaEndpointsListViewModel());
}
}
}
/// <summary>
/// //OPC UA Endpoint List
/// </summary>
public ObservableCollection<AddOpcUaEndpointsViewModel> OpcUaEndpoints
{
get => _endpoint;
set
{
if (OpcUaEndpoints == value)
{
_endpoint = value;
OnPropertyChanged("OpcUaEndpoints");
}
}
}
public string CharNameFromTB
{
get { return _charNameFromTB; }
set
{
_charNameFromTB = value;
OnPropertyChanged("CharNameFromTB");
}
}
public ICommand AddCommand
{
get { return _addCommand ?? (_addCommand = new RelayCommand(p => ExecuteAddCommand())); }
}
public ICommand SearchCommand
{
get { return _searchcommand ?? (_searchcommand = new RelayCommand(p => ExecuteSearchCommand())); }
}
private void ExecuteSearchCommand()
{
BindDataGridsearch();
}
private void BindDataGrid()
{
var opcendptsModel = opcUaEndpointsService.GetAll();
_endpoint = new ObservableCollection<AddOpcUaEndpointsViewModel>(opcendptsModel.Select(p => new AddOpcUaEndpointsViewModel(p)));
}
Please find working code. I have made some modification to your code
XAML
<DataGrid x:Name="DgLines" ItemsSource="{Binding OpcUaEndpoints}" AutoGenerateColumns="False"
SelectionMode="Extended" IsReadOnly="True" Grid.ColumnSpan="5">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Name="ckbSelectedAll"
IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked" >
<i:InvokeCommandAction Command="{Binding DataContext.CheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" />
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked" >
<i:InvokeCommandAction Command="{Binding DataContext.UncheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="cbkSelect"
IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Width="200" Header="Id" Binding="{Binding Id }" />-->
<DataGridTextColumn Width="200" Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Width="500" Header="Description" Binding="{Binding Description}"/>
<DataGridTextColumn Width="500" Header="Lines" Binding="{Binding Endpoint}"/>
</DataGrid.Columns>
</DataGrid>
c#
public class OpcUaEndpointsListViewModel : INotifyPropertyChanged
{
private static OpcUaEndpointsListViewModel _instance;
private static readonly object Padlock = new object();
private ICommand _addCommand;
//private ICommand _uncheckCommand;
private ICommand _searchcommand;
private ICommand _checkedCommand { get; set; }
private ICommand _unCheckedCommand { get; set; }
private ObservableCollection<AddOpcUaEndpointsViewModel> _endpoint;
public string _charNameFromTB;
public event PropertyChangedEventHandler PropertyChanged;
public OpcUaEndpointsListViewModel()
{
BindDataGrid();
}
public static OpcUaEndpointsListViewModel Instance
{
get
{
lock (Padlock)
{
return _instance ?? (_instance = new OpcUaEndpointsListViewModel());
}
}
}
/// <summary>
/// //OPC UA Endpoint List
/// </summary>
public ObservableCollection<AddOpcUaEndpointsViewModel> OpcUaEndpoints
{
get { return _endpoint; }
set
{
if (OpcUaEndpoints == value)
{
_endpoint = value;
OnPropertyChanged("OpcUaEndpoints");
}
}
}
public string CharNameFromTB
{
get { return _charNameFromTB; }
set
{
_charNameFromTB = value;
OnPropertyChanged("CharNameFromTB");
}
}
public ICommand AddCommand
{
get { return _addCommand ?? (_addCommand = new WpfApplication1.RelayCommand<object>(p => ExecuteAddCommand())); }
}
public ICommand SearchCommand
{
get { return _searchcommand ?? (_searchcommand = new RelayCommand<object>(p => ExecuteSearchCommand())); }
}
public ICommand CheckedCommand
{
get { return _checkedCommand ?? (_checkedCommand = new WpfApplication1.RelayCommand<object>(p => ExecuteCheckedCommand())); }
}
public ICommand UncheckedCommand
{
get { return _unCheckedCommand ?? (_unCheckedCommand = new WpfApplication1.RelayCommand<object>(p => ExecuteUnCheckedCommand())); }
}
private void ExecuteSearchCommand()
{
///BindDataGridsearch();
}
private void ExecuteCheckedCommand()
{
foreach (var item in _endpoint)
{
item.IsSelected = true;
}
}
private void ExecuteUnCheckedCommand()
{
foreach (var item in _endpoint)
{
item.IsSelected = false;
}
}
private void ExecuteAddCommand()
{
///BindDataGridsearch();
}
private void BindDataGrid()
{
_endpoint = new ObservableCollection<AddOpcUaEndpointsViewModel>();
_endpoint.Add(new AddOpcUaEndpointsViewModel { Name = "A", Description = "A", Endpoint = 1 });
_endpoint.Add(new AddOpcUaEndpointsViewModel { Name = "B", Description = "B", Endpoint = 2 });
_endpoint.Add(new AddOpcUaEndpointsViewModel { Name = "C", Description = "C", Endpoint = 3 });
_endpoint.Add(new AddOpcUaEndpointsViewModel { Name = "D", Description = "D", Endpoint = 4 });
_endpoint.Add(new AddOpcUaEndpointsViewModel { Name = "E", Description = "E", Endpoint = 5 });
}
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class AddOpcUaEndpointsViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged("Name"); }
}
private string _description;
public string Description
{
get { return _description; }
set { _description = value; OnPropertyChanged("Description"); }
}
private int _endPoint;
public int Endpoint
{
get { return _endPoint; }
set { _endPoint = value; OnPropertyChanged("Endpoint"); }
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set { _isSelected = value; OnPropertyChanged("IsSelected"); }
}
}
The DataGrid column definitions do not inherit the DataContext as they're not part of the visual tree.
You will have to use a BindingProxy¹ to get around this.
<DataGrid.Resources>
<attached:BindingProxy x:Key="proxy" Data="{Binding}"/>
</DataGrid.Resources>
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<CheckBox IsChecked="{Binding Data.IsHeaderCheckBoxChecked, Source={StaticResource proxy}}"/>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
BindingProxy
public class BindingProxy : Freezable
{
#region XAML Properties
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register(nameof(Data), typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
public object Data
{
get { return GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
#endregion
#region Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
}
Also remember that there are no UI-controls in your VM and no XXX_Clicked handler or smiliar. These belong in the code-behind file (*.xaml.cs), if necessary.
¹ You may also check this question.
I want to make a text Box Visible when clicking a DataGrid Column. I use this text box for description of a data Grid column
My two column has grid view (Item and Amount columns)
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Item" Width="*">
<DataGridTemplateColumn.CellTemplate>
<TextBlock Text="{Binding Item}" VerticalAlignment="Center"/>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Amount" Width="*">
<DataGridTemplateColumn.CellTemplate>
<TextBlock Text="{Binding Amount}" VerticalAlignment="Center"/>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I want get the sum of Column [Amount] and show it on a textbox, that is visible only when I click [Amount] Column
Please try the next code:
1. Xaml:
<Window x:Class="DataGridSoHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dataGridSoHelpAttempt="clr-namespace:DataGridSoHelpAttempt"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Title="MainWindow" Height="350" Width="525" x:Name="This">
<Window.DataContext>
<dataGridSoHelpAttempt:MainViewModel/>
</Window.DataContext>
<Grid x:Name="MyGrid">
<DataGrid x:Name="MyDataGrid" ItemsSource="{Binding DataSource}" AutoGenerateColumns="False">
<i:Interaction.Behaviors>
<dataGridSoHelpAttempt:ColumnSelectingBehavior TotalPresenterVisibility="{Binding TotalsVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</i:Interaction.Behaviors>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Comments" Binding="{Binding Comments}" Width="150"/>
<DataGridTextColumn Header="Price (click to see total)" Binding="{Binding Price, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
<TextBlock Visibility="{Binding TotalsVisibility, UpdateSourceTrigger=PropertyChanged}">
<Run Text="Total price:"></Run>
<Run Text="{Binding TotalValue, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"></Run>
</TextBlock>
</StackPanel>
</Grid></Window>
2. View models and models code:
public class MainViewModel:BaseObservableObject
{
private Visibility _visibility;
private ICommand _command;
private Visibility _totalsVisibility;
private double _totalValue;
public MainViewModel()
{
Visibility = Visibility.Collapsed;
TotalsVisibility = Visibility.Collapsed;
DataSource = new ObservableCollection<BaseData>(new List<BaseData>
{
new BaseData {Name = "Uncle Vania", Description = "A.Chekhov, play", Comments = "worth reading", Price = 25},
new BaseData {Name = "Anna Karenine", Description = "L.Tolstoy, roman", Comments = "worth reading", Price = 35},
new BaseData {Name = "The Master and Margarita", Description = "M.Bulgakov, novel", Comments = "worth reading", Price = 56},
});
}
public ICommand Command
{
get
{
return _command ?? (_command = new RelayCommand(VisibilityChangingCommand));
}
}
private void VisibilityChangingCommand()
{
Visibility = Visibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
}
public ObservableCollection<BaseData> DataSource { get; set; }
public Visibility Visibility
{
get { return _visibility; }
set
{
_visibility = value;
OnPropertyChanged();
}
}
public ObservableCollection<BaseData> ColumnCollection
{
get { return DataSource; }
}
public Visibility TotalsVisibility
{
get { return _totalsVisibility; }
set
{
_totalsVisibility = value;
OnPropertyChanged();
}
}
public double TotalValue
{
get { return ColumnCollection.Sum(x => x.Price); }
}
}
public class BaseData:BaseObservableObject
{
private string _name;
private string _description;
private string _comments;
private int _price;
public virtual string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public virtual object Description
{
get { return _description; }
set
{
_description = (string) value;
OnPropertyChanged();
}
}
public string Comments
{
get { return _comments; }
set
{
_comments = value;
OnPropertyChanged();
}
}
public int Price
{
get { return _price; }
set
{
_price = value;
OnPropertyChanged();
}
}
}
3. Behavior code:
public class ColumnSelectingBehavior : Behavior<DataGrid>
{
public static readonly DependencyProperty TotalPresenterVisibilityProperty = DependencyProperty.Register(
"TotalPresenterVisibility", typeof (Visibility), typeof (ColumnSelectingBehavior), new PropertyMetadata(Visibility.Collapsed));
public Visibility TotalPresenterVisibility
{
get { return (Visibility) GetValue(TotalPresenterVisibilityProperty); }
set { SetValue(TotalPresenterVisibilityProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Sorting += AssociatedObjectOnSorting;
}
private void AssociatedObjectOnSorting(object sender, DataGridSortingEventArgs dataGridSortingEventArgs)
{
var columnSortMemberPath = dataGridSortingEventArgs.Column.SortMemberPath;
if(columnSortMemberPath != "Price")
return;
TotalPresenterVisibility = TotalPresenterVisibility == Visibility.Visible
? Visibility.Collapsed
: Visibility.Visible;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Sorting -= AssociatedObjectOnSorting;
}
}
4. BaseObservableObject code (for .net 4.5):
public class BaseObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
{
var propName = ((MemberExpression)raiser.Body).Member.Name;
OnPropertyChanged(propName);
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(name);
return true;
}
return false;
}
}
I'll glad to help in case you will have problems wit the code. Regards.
I got checkbox column in DataGrid that populating realtime download percentage in particular row, filtered by caseRefNo. i need to add checkbox changed event to perform some action. i used InvokeCommandAction to add the action to checkbox.
I realize that when i click the checkbox for the first time, It is ok and trigger only one time. but there are two times triggered when i click second time on the same checkbox.For third time clicking the same checkbox, it triggered four time. quite scary and difficult to figure it out.
here is my viewmodel code
public class DataGridDownloadViewModel:BindableBase
{
public ObservableCollection<tblTransaction> TransList { get; private set; }
public DispatcherTimer dispatchTimer = new DispatcherTimer();
public CollectionView TransView { get; private set; }
public DelegateCommand<object> CheckCommand { get; set; }
private String _UpdatePer;
public String UpdatePercentage
{
get { return _UpdatePer; }
set { SetProperty(ref _UpdatePer, value); }
}
private string _caseId;
public string CaseID
{
get { return _caseId; }
set { SetProperty(ref _caseId, value); }
}
private string _isChecked;
public string isChecked
{
get { return _isChecked; }
set { SetProperty(ref _isChecked, value); }
}
private bool CanExecute(object args)
{
return true;
}
private void CheckBoxChecker(object args)
{
//Should Work Here
// Totally not coming to this function
CheckBox chk = (CheckBox)args;
string thichintae = chk.Name.ToString();
Console.WriteLine(thichintae);
}
public DataGridDownloadViewModel(List<tblTransaction> model)
{
CheckCommand = new DelegateCommand<object>(CheckBoxChecker, CanExecute);
dispatchTimer.Interval = TimeSpan.FromMilliseconds(3000);
dispatchTimer.Tick += dispatchTimer_Tick;
BackGroundThread bgT = Application.Current.Resources["BackGroundThread"] as BackGroundThread;
bgT.GetPercentChanged += (ss, ee) =>
{
UpdatePercentage = bgT.local_percentage.ToString();
};
bgT.GetCaseID += (ss, ee) =>
{
CaseID = bgT.local_caseRef;
};
TransList =new ObservableCollection<tblTransaction>(model);
TransView = GetTransCollectionView(TransList);
TransView.Filter = OnFilterTrans;
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var cancellationTokenSource = new CancellationTokenSource();
dispatchTimer.Start();
}
private void dispatchTimer_Tick(object sender, EventArgs e)
{
UpdateDataGrid();
}
public void UpdateDataGrid()
{
foreach (tblTransaction tran in TransList)
{
if (tran.caseRefNo == CaseID)
{
tran.incValue = int.Parse(UpdatePercentage);
}
else
{
tran.incValue = tran.incValue;
}
}
TransView.Refresh();
}
bool OnFilterTrans(object item)
{
var trans = (tblTransaction)item;
return true;
}
public CollectionView GetTransCollectionView(ObservableCollection<tblTransaction> tranList)
{
return (CollectionView)CollectionViewSource.GetDefaultView(tranList);
}
}
this is XAML for above view model.
<Window x:Class="EmployeeManager.View.DataGridDownload"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
Title="DataGridDownload" Height="600" Width="790">
<Grid>
<DataGrid HorizontalAlignment="Left" ItemsSource="{Binding TransView}" AutoGenerateColumns="False" Margin="10,62,0,0" VerticalAlignment="Top" Height="497" Width="762">
<DataGrid.Columns>
<DataGridTextColumn Header="caseRefNo" Binding="{Binding caseRefNo}" />
<DataGridTextColumn Header="subjMatr" Binding="{Binding subjMatr}" />
<DataGridTextColumn Header="Download %" Binding="{Binding incValue}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="abcdef"
Content="Please Select" IsChecked="{Binding Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction CommandParameter="{Binding ElementName=abcdef}" Command="{Binding DataContext.CheckCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding incValue,UpdateSourceTrigger=PropertyChanged}" Background="Red" Foreground="White" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Label Content="{Binding UpdatePercentage}" HorizontalAlignment="Left" Background="Blue" Foreground="White" Margin="10,10,0,0" VerticalAlignment="Top" Width="338" Height="30">
</Label>
<Button Content="Button" HorizontalAlignment="Left" Margin="672,20,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
</Window>
Here is my model
public class tblTransaction
{
public string caseRefNo { get;set;}
public string subjMatr { get; set; }
public int incValue { get; set; }
public DateTime? longTime { get; set; }
public bool IsSelected { get; set; }
}
This is picture of my form
Is it because of DispatcherTimer ? All suggestion are welcome.
df
I think I left a comment in your previous question saying that wrapping your collection to CollectionView is quite smelly.
Anyway, the TransView.Refresh(); is causing the problem in your code.
TransView.Refresh will trigger the "Checked" event for each checked checkbox. The refresh basically asking the wpf engine re-populate all the data to your CollectionView and in turn, each checked checkbox will fire the checked event all over again.
Try setting dispatchTimer.Interval to a much shorter time e.g. 300. You should be able to see the "tick" in checkbox keep flicking becoz of the TransView.Refresh.
For real, I have no idea why you don't just bind your TransList to your DataGrid and called BeginInvoke on UpdateDataGrid method.
To demo how ObservableCollection works
I used the ObservableCollection to re-wrote some part of your code. At least make things better fall in line with MVVM model.
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MainViewModel : ViewModelBase
{
public ObservableCollection<TblTransaction> TransList { get; private set; }
public DispatcherTimer DispatchTimer = new DispatcherTimer();
public MainViewModel()
{
var model = new ObservableCollection<TblTransaction>();
for (int i = 0; i < 5; i++)
{
model.Add(new TblTransaction { CaseRefNo = i.ToString(), IncValue = i, LongTime = DateTime.Now, SubjMatr = i.ToString() });
if (i == 3)
model[i].IsSelected = true;
}
DispatchTimer.Interval = TimeSpan.FromMilliseconds(200);
DispatchTimer.Tick += dispatchTimer_Tick;
TransList = model;
DispatchTimer.Start();
}
private void dispatchTimer_Tick(object sender, EventArgs e)
{
UpdateDataGrid();
}
public void UpdateDataGrid()
{
var ran = new Random();
foreach (var tran in TransList)
tran.IncValue = ran.Next(0, 100);
}
}
public class TblTransaction : ViewModelBase
{
private string caseRefNo;
private string subjMatr;
private int incValue;
private DateTime? longTime;
private bool isSelected;
public DelegateCommand<object> CheckCommand { get; set; }
public TblTransaction()
{
CheckCommand = new DelegateCommand<object>(CheckBoxChecker, (p) => true);
}
private void CheckBoxChecker(object args)
{
//Should Work Here
// Totally not coming to this function
//CheckBox chk = (CheckBox)args;
//string thichintae = chk.Name;
Console.WriteLine(args);
}
public string CaseRefNo
{
get { return caseRefNo; }
set
{
caseRefNo = value;
OnPropertyChanged();
}
}
public string SubjMatr
{
get { return subjMatr; }
set
{
subjMatr = value;
OnPropertyChanged();
}
}
public int IncValue
{
get { return incValue; }
set
{
incValue = value;
OnPropertyChanged();
}
}
public DateTime? LongTime
{
get { return longTime; }
set
{
longTime = value;
OnPropertyChanged();
}
}
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
OnPropertyChanged();
}
}
}
<Window x:Class="WpfTestProj.MainWindow"
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"
xmlns:local="clr-namespace:WpfTestProj"
xmlns:interact="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=local:MainViewModel, IsDesignTimeCreatable=False}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid HorizontalAlignment="Left" ItemsSource="{Binding TransList}" AutoGenerateColumns="False" Margin="10,62,0,0" VerticalAlignment="Top" Height="497" Width="762">
<DataGrid.Columns>
<DataGridTextColumn Header="caseRefNo" Binding="{Binding CaseRefNo}" />
<DataGridTextColumn Header="subjMatr" Binding="{Binding SubjMatr}" />
<DataGridTextColumn Header="Download %" Binding="{Binding IncValue}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox
Content="Please Select" IsChecked="{Binding Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<interact:Interaction.Triggers>
<interact:EventTrigger EventName="Checked">
<interact:InvokeCommandAction CommandParameter="{Binding Path=CaseRefNo}" Command="{Binding Path=CheckCommand}" />
</interact:EventTrigger>
</interact:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding IncValue, UpdateSourceTrigger=PropertyChanged}" Background="Red" Foreground="White" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Content="Button" HorizontalAlignment="Left" Margin="672,20,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
I'm new to linq, wpf and C#. I've managed to research my way to a functional component. I have attacked data binding successfully but I'm struggling with performance. I'm reading in a static external xml file (i.e. database) and want to display it to users using a wpf datagrid. The additional bit of info is that I'm using a user-controlled wpf combobox to filter how much data from the database is shown in the grid. I'd like to use linq to accomplish this task but I can't seem to get it to perform correctly.
C# file:
namespace ReadPipeXMLDB
{
public partial class ReadDB : Window, INotifyPropertyChanged
{
private XDocument xmlDoc = null;
const string ALL = "All";
// Constructor
public ReadDB()
{
InitializeComponent();
// Load xml
xmlDoc = XDocument.Load("DataBase.xml");
this.DataContext = this;
}
private ObservableCollection<CPipeData> _col;
public ObservableCollection<CPipeData> Col
{
get { return _col; }
set
{
if (_col == value)
return;
_col = value;
OnPropertyChanged(() => Col);
}
}
private ObservableCollection<CMfgData> _mfgCollection;
public ObservableCollection<CMfgData> MfgCollection
{
get { return _mfgCollection; }
set
{
if (_mfgCollection == value)
return;
_mfgCollection = value;
OnPropertyChanged(() => MfgCollection);
}
}
private ObservableCollection<string> _mfgNames;
public ObservableCollection<string> MfgNames
{
get { return this._mfgNames; }
set
{
if (this._mfgNames == value)
return;
this._mfgNames = value;
OnPropertyChanged(() => MfgNames);
}
}
#region Notify Event Declaration and Definition
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged<T>(Expression<Func<T>> property)
{
PropertyChangedEventHandler eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
var memberExpression = property.Body as MemberExpression;
eventHandler(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
}
}
#endregion
public class CMfgData : ReadDB
{
private string _mfgName;
//private ObservableCollection<CPipeData> _pipeDataCollection;
/*public CMfgData(string mfgName, CPipeData pipeData)
{
_mfgName = mfgName;
_pipeData = pipeData;
}*/
#region CMfgData Property Definitions
public string MfgName
{
get { return _mfgName; }
set
{
if (_mfgName == value)
return;
_mfgName = value;
OnPropertyChanged(() => MfgName);
}
}
/* public ObservableCollection<CPipeData> PipeDataCollection
{
get { return _pipeDataCollection; }
set
{
if (_pipeDataCollection == value)
return;
_pipeDataCollection = value;
OnPropertyChanged(() => PipeDataCollection);
}
}*/
#endregion
}
public class CPipeData : ReadDB
{
// PipeData Property Declarations
private string _nominal;
private string _sched;
private string _id;
private string _od;
private string _wt;
public CPipeData()
{
_nominal = "";
_sched = "";
_id = "";
_od = "";
_wt = "";
}
// Constructor
public CPipeData(string nominal, string sched, string id, string od, string wt)
{
_nominal = nominal;
_sched = sched;
_id = id;
_od = od;
_wt = wt;
}
#region CPipeData Property Definitions
public string Nominal
{
get { return _nominal; }
set
{
if (_nominal == value)
return;
_nominal = value;
OnPropertyChanged(() => Nominal);
}
}
public string Sched
{
get { return _sched; }
set
{
if (_sched == value)
return;
_sched = value;
OnPropertyChanged(() => Sched);
}
}
public string ID
{
get { return _id; }
set
{
if (_id == value)
return;
_id = value;
OnPropertyChanged(() => ID);
}
}
public string OD
{
get { return _od; }
set
{
if (_od == value)
return;
_od = value;
OnPropertyChanged(() => OD);
}
}
public string WT
{
get { return _wt; }
set
{
if (_wt == value)
return;
_wt = value;
OnPropertyChanged(() => WT);
}
}
#endregion
}
private void mfgrComboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Update database grid
if (mfgrComboBox1.SelectedValue is string)
{
PopulateGrid(mfgrComboBox1.SelectedValue as string);
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
/*MfgCollection = new ObservableCollection<CMfgData>(
from mfg in xmlDoc.Root.Elements("Mfg")
//where mfg.Attribute("name").Value == comboValue
select new CMfgData
{
MfgName = mfg.Attribute("name").Value,
PipeDataCollection =
new ObservableCollection<CPipeData>
(from pipe in mfg.Elements("pipe")
select new CPipeData
{
Nominal = pipe.Element("Nominal").Value,
Sched = pipe.Element("Schedule").Value,
ID = pipe.Element("ID").Value,
OD = pipe.Element("OD").Value,
WT = pipe.Element("Wall_Thickness").Value
})
});*/
}
private void mfgrComboBox1_Loaded(object sender, RoutedEventArgs e)
{
// Make sure xml document has been loaded
if (xmlDoc != null)
{
ObservableCollection<string> tempCollection = new ObservableCollection<string>(
from n in xmlDoc.Root.Elements("Mfg").Attributes("name")
select n.Value);
// Add the additional "All" filter
tempCollection.Insert(0, ALL);
// Assign list to member property. This is done last so the property event gets fired only once
MfgNames = tempCollection;
PopulateGrid(ALL);
}
}
private void PopulateGrid(string comboValue)
{
if (mfgrComboBox1.Items.IndexOf(comboValue) > -1)
{
Col = new ObservableCollection<CPipeData>(
from mfg in xmlDoc.Root.Elements("Mfg")
where mfg.Attribute("name").Value == comboValue
from pipe in mfg.Elements("pipe")
select new CPipeData
{
Nominal = pipe.Element("Nominal").Value,
Sched = pipe.Element("Schedule").Value,
ID = pipe.Element("ID").Value,
OD = pipe.Element("OD").Value,
WT = pipe.Element("Wall_Thickness").Value
});
}
}
}
}
Xaml:
<Window x:Class="ReadPipeXMLDB.ReadDB"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Standard Pipe Sizes" Height="849" Width="949" Loaded="Window_Loaded">
<Grid>
<!-- Manufactuer filter combobox -->
<ComboBox Name="mfgrComboBox1"
ItemsSource="{Binding Path=MfgNames}"
SelectedIndex="0"
Height="23" Width="286"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="20,20,0,0" SelectionChanged="mfgrComboBox1_SelectionChanged" Loaded="mfgrComboBox1_Loaded" />
<!-- Units combobox -->
<ComboBox Height="23" HorizontalAlignment="Left" Margin="320,20,0,0" Name="dimensionsComboBox2" VerticalAlignment="Top" Width="87" />
<!-- Pipe database display grid -->
<DataGrid Name="dataGrid1" IsReadOnly="True" AutoGenerateColumns="False" Margin="20,60,20,20" ItemsSource="{Binding Col}">
<DataGrid.Columns>
<DataGridTextColumn Header="Nominal" Binding="{Binding Path=Nominal}"/>
<DataGridTextColumn Header="Schedule" Binding="{Binding Path=Sched}"/>
<DataGridTextColumn Header="ID" Binding="{Binding Path=ID}"/>
<DataGridTextColumn Header="OD" Binding="{Binding Path=OD}"/>
<DataGridTextColumn Header="Wall Thickness" Binding="{Binding Path=WT}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
XML:
<DBRoot>
<Mfg name="A Manufac">
<pipe>
<Nominal>testdata</Nominal>
<Schedule>testdata</Schedule>
<OD>testdata</OD>
<Wall_Thickness>testdata</Wall_Thickness>
<ID>testdata</ID>
</pipe>
<pipe>
<Nominal>testdata</Nominal>
<Schedule>testdata</Schedule>
<OD>testdata</OD>
<Wall_Thickness>testdata</Wall_Thickness>
<ID>testdata</ID>
</pipe>
</Mfg>
<Mfg name="B Manufac">
<pipe>
<Nominal>testdata</Nominal>
<Schedule>testdata</Schedule>
<OD>testdata</OD>
<Wall_Thickness>testdata</Wall_Thickness>
<ID>testdata</ID>
</pipe>
<pipe>
<Nominal>testdata</Nominal>
<Schedule>testdata</Schedule>
<OD>testdata</OD>
<Wall_Thickness>testdata</Wall_Thickness>
<ID>testdata</ID>
</pipe>
</Mfg>
</DBRoot>
The PopulateGrid call is slow because I create a new ObservableCollection every time the combobox changes value. I'm not use to working with collections and linq so if someone can offer my a more robust alternative I'd appreciate it!
You can save yourself some trouble by binding directly to the XML file:
XAML
<Grid>
<Grid.DataContext>
<XmlDataProvider Source="DataBase.xml"/>
</Grid.DataContext>
<StackPanel>
<ComboBox ItemsSource="{Binding XPath=/DBRoot/Mfg}" Name="comboBox" SelectedIndex="0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding XPath=#name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<DataGrid ItemsSource="{Binding ElementName=comboBox, Path=SelectedItem}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding XPath=Nominal}" Header="Nominal"/>
<DataGridTextColumn Binding="{Binding XPath=Schedule}" Header="Schedule"/>
<DataGridTextColumn Binding="{Binding XPath=OD}" Header="OD"/>
<DataGridTextColumn Binding="{Binding XPath=Wall_Thickness}" Header="Wall Thickness"/>
<DataGridTextColumn Binding="{Binding XPath=ID}" Header="ID"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Grid>
Oh my goodness. Your problem starts with ObservableCollection<string> MfgNames. String has no intelligence and you are building data from scratch every time. Use a class.
public class Mfg
{
public string Name { get; private set; }
public ObservableCollection <CPipeData> pipes { ....
Then in the detail you just bind
ItemsSounce="{binding ElementName=cbMfg Path=SelectedItem.Pipes}"
Look up Master Detail on MSDN.Microsoft.Com
If several Mfg use the same pipe then you would create a hashset with the relationship and pass the hashset to Mfg and use LINQ the filter from that single hashset. Override GetHash.