I have a DataGrid that its source is DB based. the code is not in MVVM.
Now, i need the DataGridComboBoxColumn source to changed based on a different DataGridComboBoxColumn value thats in the same DataGrid- I'm sure there is a simple solution, but still, couldnt figure it out- how do i do that?
My code:
XAML:
<DataGridComboBoxColumn x:Name="active_idnt_deviceCmb" SelectedValueBinding="{Binding idnt_linked_io_device}" DisplayMemberPath="correct_idnt_active_logic_device" SelectedValuePath="idnt_active_device" Header="input id" Width="80"></DataGridComboBoxColumn>
<DataGridComboBoxColumn x:Name="active_device_addressCmb" ElementStyle="{StaticResource MyComboBoxStyle}" SelectedValueBinding="{Binding idnt_linked_io_device}" DisplayMemberPath="active_device_address" SelectedValuePath="active_device_address" Header="Relay Address" Width="65"><DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<EventSetter Event="SelectionChanged" Handler="changeDeviceAddress" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
cs:
private void changeDeviceAddress(object sender, SelectionChangedEventArgs e)
{
HelpDataSet.ACTIVE_IO_DEVICESDataTable dtActiveIo = new HelpDataSet.ACTIVE_IO_DEVICESDataTable();
var comboBox = sender as ComboBox;
if (comboBox.SelectedValue != null)
{
AppHelp.ActiveIODeviceAdapter.ClearBeforeFill = true;
AppHelp.ActiveIODeviceAdapter.FillByIdntRelayAddress(dtActiveIo, comboBox.SelectedValue.ToString());
active_idnt_deviceCmb.ItemsSource = dtActiveIo.DefaultView;
}
}
but it changes the whole column source and not just of the specific cell in the row.
here is solution which doesn't use the MVVM approach at all, it based on the WPF selection oriented behavior. Please keep in mind that all models just presents a data grid rows, they does'n have any additional functionality. The Source collection collecting is triggered in the behavior by a combo selection trigger.
Here is the code:
1. XAML code:
<Window x:Class="ComboWithoutCodeBehind.MainWindow"
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:comboWithoutCodeBehind="clr-namespace:ComboWithoutCodeBehind"
Title="MainWindow" Height="350" Width="525" x:Name="This">
<Grid >
<DataGrid x:Name="SelectDataGrid"
ItemsSource="{Binding ElementName=This, Path=Persons}" HorizontalAlignment="Left" VerticalAlignment="Top" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn x:Name="dgCheckBox" Header="Select" Width="45" Binding="{Binding IsChecked}"/>
<DataGridTextColumn Header="FIRST NAME" Width="125" Binding="{Binding FNAME}"/>
<DataGridTextColumn Header="LAST NAME" Width="125" Binding="{Binding LNAME}"/>
<DataGridTemplateColumn Header="Selection" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0" x:Name="DataGridTemplateColumnComboBox" SelectedIndex="0" ItemsSource="{Binding ElementName=This, Path=ServersCollection}"
DisplayMemberPath="ServerName"></ComboBox>
<ComboBox Grid.Column="1" DisplayMemberPath="DbName">
<i:Interaction.Behaviors>
<comboWithoutCodeBehind:ItemsSourcePosessingBehavior
SourceProvidingFactory="{Binding ElementName=This, Path=MainSourceProvidingFactory}"
SiblingComboBox="{Binding ElementName=DataGridTemplateColumnComboBox}"/>
</i:Interaction.Behaviors>
</ComboBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
2. Code behind, models and factory code:
public partial class MainWindow : Window
{
public static readonly DependencyProperty PersonsProperty = DependencyProperty.Register("Persons",
typeof (ObservableCollection<Person>), typeof (MainWindow),
new PropertyMetadata(default(ObservableCollection<Person>)));
public static readonly DependencyProperty MainSourceProvidingFactoryProperty =
DependencyProperty.Register("MainSourceProvidingFactory", typeof (SourceProvidingFactory),
typeof (MainWindow), new PropertyMetadata(default(SourceProvidingFactory)));
public ObservableCollection<Person> Persons
{
get { return (ObservableCollection<Person>)GetValue(PersonsProperty); }
set { SetValue(PersonsProperty, value); }
}
public SourceProvidingFactory MainSourceProvidingFactory
{
get { return (SourceProvidingFactory)GetValue(MainSourceProvidingFactoryProperty); }
set { SetValue(MainSourceProvidingFactoryProperty, value); }
}
public MainWindow()
{
MainSourceProvidingFactory = new SourceProvidingFactory(GetCollection);
InitSources();
InitializeComponent();
}
private ObservableCollection<DbDetails> GetCollection(object arg)
{
//you can perform your db relate logic here
var sereverDetails = arg as ServerDetails;
return sereverDetails == null ? null : new ObservableCollection<DbDetails>(sereverDetails.DbDetailses);
}
private void InitSources()
{
var l = new List<Person>
{
new Person {FNAME = "John", LNAME = "W"},
new Person {FNAME = "George", LNAME = "R"},
new Person {FNAME = "Jimmy", LNAME = "B"},
new Person {FNAME = "Marry", LNAME = "B"},
new Person {FNAME = "Ayalot", LNAME = "A"},
};
Persons = new ObservableCollection<Person>(l);
ServersCollection = new ObservableCollection<ServerDetails>(new List<ServerDetails>
{
new ServerDetails
{
ServerName = "A",
DbDetailses = new List<DbDetails>
{
new DbDetails {DbName = "AA"},
new DbDetails {DbName = "AB"},
new DbDetails {DbName = "AC"},
}
},
new ServerDetails
{
ServerName = "B",
DbDetailses = new List<DbDetails>
{
new DbDetails {DbName = "BA"},
new DbDetails {DbName = "BB"},
new DbDetails {DbName = "BC"},
}
},
new ServerDetails
{
ServerName = "C",
DbDetailses = new List<DbDetails>
{
new DbDetails {DbName = "CA"},
new DbDetails {DbName = "CB"},
}
}
});
}
public ObservableCollection<ServerDetails> ServersCollection { get; set; }
}
public class SourceProvidingFactory
{
public SourceProvidingFactory(Func<object, ObservableCollection<DbDetails>> action)
{
GetCollection = action;
}
public Func<object, ObservableCollection<DbDetails>> GetCollection { get; set; }
}
public class Person : BaseObservableObject
{
private string _lName;
private string _fName;
private bool _checked;
public bool IsChecked
{
get { return _checked; }
set
{
_checked = value;
OnPropertyChanged();
}
}
public string LNAME
{
get { return _lName; }
set
{
_lName = value;
OnPropertyChanged();
}
}
public string FNAME
{
get { return _fName; }
set
{
_fName = value;
OnPropertyChanged();
}
}
}
public class ServerDetails : BaseObservableObject
{
private string _serverName;
public string ServerName
{
get { return _serverName; }
set
{
_serverName = value;
OnPropertyChanged();
}
}
public List<DbDetails> DbDetailses { get; set; }
}
public class DbDetails : BaseObservableObject
{
private string _dbName;
public string DbName
{
get { return _dbName; }
set
{
_dbName = value;
OnPropertyChanged();
}
}
}
3. Behavior code:
public class ItemsSourcePosessingBehavior : Behavior<ComboBox>
{
public static readonly DependencyProperty SiblingComboBoxProperty = DependencyProperty.Register(
"SiblingComboBox", typeof(ComboBox), typeof(ItemsSourcePosessingBehavior), new PropertyMetadata(default(ComboBox)));
public ComboBox SiblingComboBox
{
get { return (ComboBox)GetValue(SiblingComboBoxProperty); }
set { SetValue(SiblingComboBoxProperty, value); }
}
public static readonly DependencyProperty SourceProvidingFactoryProperty = DependencyProperty.Register(
"SourceProvidingFactory", typeof (SourceProvidingFactory), typeof (ItemsSourcePosessingBehavior), new PropertyMetadata(default(SourceProvidingFactory)));
public SourceProvidingFactory SourceProvidingFactory
{
get { return (SourceProvidingFactory) GetValue(SourceProvidingFactoryProperty); }
set { SetValue(SourceProvidingFactoryProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
SiblingComboBox.SelectionChanged += SiblingComboBoxOnSelectionChanged;
SiblingComboBox.Loaded += SiblingComboBoxOnLoaded;
}
private void SiblingComboBoxOnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
AssociatedObject.ItemsSource = null;
var siblingCombo = sender as ComboBox;
InitAssociatedObjectItemsSource(siblingCombo);
}
private void SiblingComboBoxOnSelectionChanged(object sender, SelectionChangedEventArgs selectionChangedEventArgs)
{
AssociatedObject.ItemsSource = null;
var siblingCombo = sender as ComboBox;
InitAssociatedObjectItemsSource(siblingCombo);
}
private void InitAssociatedObjectItemsSource(ComboBox siblingCombo)
{
if (siblingCombo == null)
{
return;
}
if (SourceProvidingFactory == null)
{
return;
}
AssociatedObject.ItemsSource = SourceProvidingFactory.GetCollection(siblingCombo.SelectedItem);
}
protected override void OnDetaching()
{
base.OnDetaching();
SiblingComboBox.SelectionChanged -= SiblingComboBoxOnSelectionChanged;
SiblingComboBox.Loaded -= SiblingComboBoxOnLoaded;
}
}
This is a complete solution just copy/past...
I'll be glad to help if you will have any problems with the code.
Regards.
I think it's a very basic problem we face in such scenario. now as you don't have problem with display and binding I'll just write down what I think the problem is.
Don't use active_idnt_deviceCmb.ItemsSource this will reset all the Combobox Item Source. You want different option in different rows based on the other column value. So you need to maintain one to one relationship for each record to each dropdown.
The best way to do so is what ever class is binded to per record you need to create that many data sources. if List<MyRecord> is itemsource of grid then design the class like:
public class MyRecord : INotifyPropertyChanged
{
private string a;
public string A
{
get { return a; }
set { a = value;
OnPropertyChanged("A");
OnPropertyChanged("SecondList");
}
}
private string b;
public string B
{
get { return b; }
set { b = value;
OnPropertyChanged("B");
}
}
private List<ComboBoxItem> firstList;
public List<ComboBoxItem> FirstList
{
get { return firstList; }
set { firstList = value;
OnPropertyChanged("FirstList");
}
}
private List<ComboBoxItem> secondList;
public List<ComboBoxItem> SecondList
{
get { return secondList.Where(x=>x.value.StartsWith(A)).ToList(); }
set { secondList = value;
OnPropertyChanged("SecondList");
}
}
}
PS: First column of grid is bind with A, second with B, and firstlist and secondlist is bind to dropdown of both columns. every time I select the value in first column it will reflected back to property A and at that point I'm refreshing the secondlist(which also contain filter logic also, or you can call filter logic and refresh secondlist separately).
As this second list is bind with only one record only one dropdown will reset.
XAML Code for understanding:
<DataGridComboBoxColumn x:Name="active_idnt_deviceCmb"
SelectedValuePath="value"
DisplayMemberPath="display"
SelectedValueBinding="{Binding A}"
Header="input id" Width="80">
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=FirstList}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
<DataGridComboBoxColumn
SelectedValuePath="value"
DisplayMemberPath="display"
SelectedValueBinding="{Binding B}"
Header="Relay Address" Width="65">
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=SecondList}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
Combobox item source class(just to understand DataGridComboBoxColumn binding):
public class ComboBoxItem
{
public ComboBoxItem(string a)
{
display = value = a;
}
public string display { get; set; }
public string value { get; set; }
}
hopefully You'll be able to fit this code in your application. The main thing is One to One relationship that is important.
Related
I'd like to access ComboBox items (which are defined in another class) in MainWindow.xaml.cs, but I can't.
I'm new to C# and WPF. The purpose of this code is to get a selected ComboBox item as a search key. I have copied from many example codes on the Internet and now I'm completely lost. I don't even know which part is wrong. So, let me show the entire codes (sorry):
MainWindow.xaml:
<Window x:Class="XY.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:local="clr-namespace:XY"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="454.4">
<Grid>
<DataGrid ItemsSource="{Binding channels}"
SelectedItem="{Binding SelectedRow, Mode=TwoWay}"
Margin="0,0,0,-0.2">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding name}"
Header="Channel" Width="Auto"/>
<DataGridTemplateColumn Width="100" Header="Point Setting">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="piontsComboBox"
ItemsSource="{Binding DataContext.points,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
SelectionChanged="PrintText"
DisplayMemberPath="name"
SelectedValuePath="name"
Margin="5"
SelectedItem="{Binding DataContext.SelectedPoint,
RelativeSource={RelativeSource AncestorType={x:Type Window}},
Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<TextBox x:Name="tb" Width="140" Height="30" Margin="10,250,200,30"></TextBox>
<Button x:Name="Browse_Button" Content="Browse" Margin="169,255,129.6,0"
Width="75" Click="Browse_Button_Click" Height="30" VerticalAlignment="Top"/>
</Grid>
MainWindow.xaml.cs:
using System;
using System.Windows;
using System.Windows.Controls;
namespace XY
{
public partial class MainWindow : Window
{
public GridModel gridModel { get; set; }
public MainWindow()
{
InitializeComponent();
gridModel = new GridModel();
this.DataContext = gridModel;
}
private void Browse_Button_Click(object sender, RoutedEventArgs e)
{
WakeupClass clsWakeup = new WakeupClass();
clsWakeup.BrowseFile += new EventHandler(gridModel.ExcelFileOpen);
clsWakeup.Start();
}
void PrintText(object sender, SelectionChangedEventArgs args)
{
//var item = pointsComboBox SelectedItem as Point;
//if(item != null)
//{
// tb.Text = "You selected " + item.name + ".";
//}
MessageBox.Show("I'd like to show the item.name in the TextBox.");
}
}
public class WakeupClass
{
public event EventHandler BrowseFile;
public void Start()
{
BrowseFile(this, EventArgs.Empty);
}
}
}
Point.cs:
namespace XY
{
public class Point : ViewModelBase
{
private string _name;
public string name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("name");
}
}
private int _code;
public int code
{
get { return _code; }
set
{
_code = value;
OnPropertyChanged("code");
}
}
}
}
Record.cs:
namespace XY
{
public class Record : ViewModelBase
{
private string _name;
public string name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("name");
}
}
private int _PointCode;
public int PointCode
{
get { return _PointCode; }
set
{
_PointCode = value;
OnPropertyChanged("PointCode");
}
}
private Record _selectedRow;
public Record selectedRow
{
get { return _selectedRow; }
set
{
_selectedRow = value;
OnPropertyChanged("SelectedRow");
}
}
private Point _selectedPoint;
public Point SelectedPoint
{
get { return _selectedPoint; }
set
{
_selectedPoint = value;
_selectedRow.PointCode = _selectedPoint.code;
OnPropertyChanged("SelectedRow");
}
}
}
}
ViewModelBase.cs:
using System.ComponentModel;
namespace XY
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
}
GridModel.cs:
using System.Collections.ObjectModel;
using System.Windows;
namespace XY
{
public class GridModel : ViewModelBase
{
public ObservableCollection<Record> channels { get; set; }
public ObservableCollection<Point> points { get; set; }
public GridModel()
{
channels = new ObservableCollection<Record>() {
new Record {name = "High"},
new Record {name = "Middle"},
new Record {name = "Low"}
};
}
internal void ExcelFileOpen(object sender, System.EventArgs e)
{
points = new ObservableCollection<Point> { new Point { } };
points.Add(new Point { name = "point1", code = 1 });
points.Add(new Point { name = "point2", code = 2 });
points.Add(new Point { name = "point3", code = 3 });
MessageBox.Show("Assume that Excel data are loaded here.");
}
}
}
The procedure goes like:
Click on the "Browse" button to load the data.
Click on the 1st column "Channel" to sort the list (This is a bug, but if you don't, the "Point Setting" items won't show up).
Click on the "Point Setting" ComboBox to select the items (point1, point2, ..., etc.).
This code is supposed to show the selected item name in the TextBox.
If everything is in MainWindow.xaml.cs, the ComboBox items could be accessed. Since I split the codes into different classes, it has not been working. Please help me. Any suggestion would be helpful.
Your binding does work. You can make use of the sender object to achieve what you wanted.
void PrintText(object sender, SelectionChangedEventArgs args)
{
var comboBox = sender as ComboBox;
var selectedPoint = comboBox.SelectedItem as Point;
tb.Text = selectedPoint.name;
}
The problem is that the DataGridColumn is not part of the WPF logical tree and so your relative source binding will not work. The only way to get your binding to work is a type of kluge (very common with WPF in my experience). Create a dummy element that is in the logical tree and then reference that.
So
<FrameworkElement x:Name="dummyElement" Visibility="Collapsed"/>
<DataGrid ItemsSource="{Binding channels}"
SelectedItem="{Binding SelectedRow, Mode=TwoWay}"
Margin="0,0,0,-0.2">
Then your binding will look like this
<ComboBox x:Name="piontsComboBox"
ItemsSource="{Binding DataContext.points,
Source={x:Reference dummyElement}}"
SelectionChanged="PrintText"
DisplayMemberPath="name"
SelectedValuePath="name"
Margin="5"
SelectedItem="{Binding DataContext.SelectedPoint,
Source={x:Reference dummyElement},
Mode=TwoWay}"/>
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.
Having some problems displaying strings in a datagrid.
To explain the code: I am binding a collection of Soldiers to a ComboBox. A Soldier has its own collection of weapons.
When I select a specific soldier in the ComboBox, I want that soldier's weapons displayed in the datagrid. I believe I'm binding correctly, but the datagrid always comes up blank. Anybody know what i'm doing wrong?
XAML
<Grid>
<ComboBox x:Name="Character_ComboBox" HorizontalAlignment="Left" VerticalAlignment="Top" Width="328" Height="25">
</ComboBox>
</Grid>
<DataGrid x:Name="Character_items_datagrid" ItemsSource="{Binding ElementName=Character_ComboBox, Path= SelectedItem.Equipment, Mode=OneWay}" Margin="328,0,0,0" Grid.RowSpan="2" >
<DataGrid.Columns>
<DataGridTextColumn Header="Primary" Binding="{Binding Primary, Mode=TwoWay}" FontWeight="Bold" Foreground="Black" Width="0.1*"></DataGridTextColumn>
<DataGridTextColumn Header ="Secondary" Binding="{Binding Secondary, Mode=TwoWay}" Width="0.1*"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Soldier Class
public class Soldier
{
public string Soldier_Class { get; set; }
public ObservableCollection<Weapons> Equipment { get; set; }
}
Weapons Class
public class Weapons
{
string Primary { get; set; }
string Secondary { get; set; }
public Weapons(string primary, string secondary)
{
this.Primary = primary;
this.Secondary = secondary;
}
}
MainWindow
public ObservableCollection<Soldier> squad_members = new ObservableCollection<Soldier>();
public MainWindow()
{
InitializeComponent();
squad_members.Add(new Soldier() { Soldier_Class = "Assult Soldier", Equipment = new ObservableCollection<Weapons>() { new Weapons("M4 Rifle", "Compact 45 Pistol")}});
squad_members.Add(new Soldier() { Soldier_Class = "SMG Soldier", Equipment = new ObservableCollection<Weapons>() { new Weapons("RPK Machine Gun", "HK Shotgun"), new Weapons("SAW Machine Gun", "Compact 45 Pistol")}});
squad_members.Add(new Soldier() { Soldier_Class = "Juggernaut", Equipment = new ObservableCollection<Weapons>() { new Weapons("MP5", "Bowie Knife") }});
Binding comboBinding = new Binding();
comboBinding.Source = squad_members;
BindingOperations.SetBinding(Character_ComboBox, ComboBox.ItemsSourceProperty, comboBinding);
Character_ComboBox.DisplayMemberPath = "Soldier_Class";
Character_ComboBox.SelectedValuePath = "Soldier_Class";
}
Result:
You need to make properties in the model public for binding to be able to work :
public class Weapons
{
public string Primary { get; set; }
public string Secondary { get; set; }
.....
}
Your DataGrid looks populated with items correctly, just the properties of each item are not correctly displayed in the columns. This is indication that binding engine can't access the item's properties due to it's private accessibility.
Your primary problem is the public access modifier, as har07 wrote.
There are a lot of other things you can improve as well. Implement INotifyPropertyChanged for your classes, so any change to the properties is immediately reflected by the UI. Without compelling reasons, do not create bindings in code. Use a ViewModel to bind to, instead of binding directly to elements like ComboBox.SelectedItem. Set AutoGenerateColumns to false if you want to style your columns (your code would produce four columns). Use Grid.ColumnDefinitions instead of assigning a fixed margin.
Models:
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace WpfApplication1.ViewModels
{
public class SquadViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<Soldier> _squadMembers;
public ObservableCollection<Soldier> SquadMembers { get { return _squadMembers; } set { _squadMembers = value; OnPropertyChanged("SquadMembers"); } }
private Soldier _selectedSoldier;
public Soldier SelectedSoldier { get { return _selectedSoldier; } set { _selectedSoldier = value; OnPropertyChanged("SelectedSoldier"); } }
public SquadViewModel()
{
SquadMembers = new ObservableCollection<Soldier>()
{
new Soldier() { SoldierClass = "Assult Soldier", Equipment = new ObservableCollection<Weapon>() { new Weapon("M4 Rifle", "Compact 45 Pistol") } },
new Soldier() { SoldierClass = "SMG Soldier", Equipment = new ObservableCollection<Weapon>() { new Weapon("RPK Machine Gun", "HK Shotgun"), new Weapon("SAW Machine Gun", "Compact 45 Pistol") } },
new Soldier() { SoldierClass = "Juggernaut", Equipment = new ObservableCollection<Weapon>() { new Weapon("MP5", "Bowie Knife") } }
};
}
}
public class Soldier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _soldierClass;
public string SoldierClass { get { return _soldierClass; } set { _soldierClass = value; OnPropertyChanged("SoldierClass"); } }
private ObservableCollection<Weapon> _equipment;
public ObservableCollection<Weapon> Equipment { get { return _equipment; } set { _equipment = value; OnPropertyChanged("Equipment"); } }
}
public class Weapon : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _primary;
string Primary { get { return _primary; } set { _primary = value; OnPropertyChanged("Primary"); } }
private string _secondary;
string Secondary { get { return _secondary; } set { _secondary = value; OnPropertyChanged("Secondary"); } }
public Weapon(string primary, string secondary)
{
this.Primary = primary;
this.Secondary = secondary;
}
}
}
Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
Title="MainWindow" Height="350" Width="580">
<Window.DataContext>
<vm:SquadViewModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="CbxCharacter" HorizontalAlignment="Left" VerticalAlignment="Top" Width="328" Height="25"
ItemsSource="{Binding SquadMembers}" SelectedItem="{Binding SelectedSoldier}"
DisplayMemberPath="SoldierClass" SelectedValuePath="SoldierClass"/>
<DataGrid x:Name="DgCharacterItems" ItemsSource="{Binding SelectedSoldier.Equipment, Mode=OneWay}" Grid.Column="1" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Primary" Binding="{Binding Primary, Mode=TwoWay}" FontWeight="Bold" Foreground="Black" Width="*" />
<DataGridTextColumn Header="Secondary" Binding="{Binding Secondary, Mode=TwoWay}" Width="*" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
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.