Changes in SelectedItem of ComboBox alter ItemsSource - c#

I have a ComboBox and a DataGrid in my XAML:
<ComboBox SelectedItem="{Binding SelectedFamily}" ItemsSource="{Binding FamilyList}" IsSynchronizedWithCurrentItem="True"/>
<DataGrid ItemsSource="{Binding FamilyInfoGrid}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Age" Binding="{Binding Age}" />
</DataGrid.Columns>
</DataGrid>
This is my viewmodel:
public class MainWindowViewModel : INotifyPropertyChanged
{
private ReadOnlyObservableCollection<Family> _familyList;
public ReadOnlyObservableCollection<Family> FamilyList
{
get { return _familyList; }
}
private Family _selectedFamily;
public Family SelectedFamily
{
get
{
return _selectedFamily;
}
set
{
_familyInfoGrid.Clear();
_familyInfoGrid.Add(value.Kid);
_familyInfoGrid.Add(value.Parent2);
_familyInfoGrid.Add(value.Parent1);
RaisePropertyChangedEvent();
}
}
private ObservableCollection<Person> _familyInfoGrid = new ObservableCollection<Person>();
public ObservableCollection<Person> FamilyInfoGrid
{
get { return _familyInfoGrid; }
set { _familyInfoGrid = value; RaisePropertyChangedEvent(); }
}
public MainWindowViewModel()
{
var fam1 = new Family("Smith", new Person("Jim", 31), new Person("Eve", 29), new Person("Tom", 2));
var fam2 = new Family("Miller", new Person("Joe", 35), new Person("Sue", 33), new Person("Kim", 8));
_familyList = new ReadOnlyObservableCollection<Family>(new ObservableCollection<Family>() { fam1, fam2 } );
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
which means the DataGrid always gets updated to the currently selected item of the combobox - as it should.
This my model:
public class Family
{
public string Name { get; set; }
public Person Kid { get; set; }
public Person Parent1 { get; set; }
public Person Parent2 { get; set; }
public Family(string name, Person parent1, Person parent2, Person kid)
{
Name = name; Parent1 = parent1; Parent2 = parent2; Kid = kid;
}
public override string ToString()
{
return Name;
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name; Age = age;
}
}
The problem is that changes that are made within the DataGrid are stored in the _familyList, but I want the _familyList to be immutable and only be able to edit (and use) the current fields of the DataGrid temporarily (so Mode=OneWay is not an option either).

Thanks to #mm8 I managed to find a solution. The currently selected item of the combobox has to be a "deep copied":
public Family SelectedFamily
{
set
{
var fam = new Family(
new string(value.Name.ToCharArray()),
new Person(value.Parent1.Name, value.Parent1.Age),
new Person(value.Parent2.Name, value.Parent2.Age),
new Person(value.Kid.Name, value.Kid.Age));
_familyInfoGrid.Clear();
_familyInfoGrid.Add(fam.Kid);
_familyInfoGrid.Add(fam.Parent2);
_familyInfoGrid.Add(fam.Parent1);
RaisePropertyChangedEvent();
}
}

Related

How can I have the respective object in a CollectionView?

I'm trying to use a dynamically created layout using CollectionView to show a series of properties of a class, all based on a list and I want to make it so one of the properties is a Combobox. How do I know what object the ComboBox needs to refer to?
Here is my CollectionView:
<CollectionView x:Name="taskList">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:Task">
<VerticalStackLayout Margin="15">
<Entry Text="{Binding name}" IsReadOnly="True" />
<Entry Text="{Binding departmentsString}" IsReadOnly="True"/>
<HorizontalStackLayout>
<inputs:SfComboBox BackgroundColor="Black" TextColor="Green" DropDownIconColor="Green"/>
<Entry Text="{Binding deadline}" IsReadOnly="True" />
<Entry Text="{Binding author.fullName}" IsReadOnly="True"/>
</HorizontalStackLayout>
<Entry Text="{Binding description}" IsReadOnly="True" />
</VerticalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
It has its ItemsSource declared like this:
taskList.ItemsSource = tasks;
tasks being:
ObservableCollection<Classes.Task> tasks { get; set; }
Here is the Task class:
public class Task
{
public Task(string name, List<string> departments, Status status, DateOnly deadline, Employee author, string description)
{
this.name = name;
this.departments = departments;
this.status = status;
this.deadline = deadline;
this.author = author;
this.description = description;
}
public string name { get; private set; }
public List<string> departments { get; private set; } = new List<string>();
public string departmentsString
{
get
{
string _ = "";
foreach (var department in departments)
{
_ += department + (department == departments.Last() ? "": ", ");
}
return _;
}
}
public Status status { get; private set; }
public DateOnly deadline { get; private set; }
public Employee? author { get; set; }
public string description { get; private set; }
public List<Employee> employees { get; private set; } = new List<Employee>();
public void AddEmployee(Employee employee)
{
if (departments.Contains(employee.department))
{
employees.Add(employee);
}
}
}
How do I make it so I can determine the instance of the class Task depending on which ComboBox I change?
Here is what the UI looks like:
Trying to bind the combobox to the Status property
You can try to set a data list for property ItemsSource of SfComboBox and bind a field to property SelectedItem of SfComboBox.
Suppose you would bind departments to the ItemsSource of SfComboBox, then we need to add a field (e.g. SelectedItem) to bind to property SelectedItem of SfComboBox:
Then we need to implement interface INotifyPropertyChanged for MyTask.cs and add field SelectedItem.(To prevent conflicts with the Task class in my project, I named it MyTask)
//add SelectedItem here
private string _selectedItem;
public string SelectedItem
{
get => _selectedItem;
set => SetProperty(ref _selectedItem, value);
}
The whole code of MyTask
public class MyTask: INotifyPropertyChanged
{
public MyTask(string name, List<string> departments, int status, DateTime deadline, Employee author, string description)
{
this.name = name;
this.departments = departments;
this.status = status;
this.deadline = deadline;
this.author = author;
this.description = description;
}
//add SelectedItem here
private string _selectedItem;
public string SelectedItem
{
get => _selectedItem;
set => SetProperty(ref _selectedItem, value);
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
public string name { get; set; }
public List<string> departments { get; private set; } = new List<string>();
public string departmentsString
{
get
{
string _ = "";
foreach (var department in departments)
{
_ += department + (department == departments.Last() ? "" : ", ");
}
return _;
}
}
public int status { get; private set; }
public DateTime deadline { get; private set; }
public Employee? author { get; set; }
public string description { get; private set; }
public List<Employee> employees { get; private set; } = new List<Employee>();
public void AddEmployee(Employee employee)
{
if (departments.Contains(employee.department))
{
employees.Add(employee);
}
}
}
Then we can use like this:
<editors:SfComboBox BackgroundColor="Black" TextColor="Green"
DropDownIconColor="Green"
WidthRequest="250"
ItemsSource="{Binding departments}"
SelectedItem="{Binding SelectedItem}"
/>
Note:
Then if we change the option of SfComboBox , the value of SelectedItem will also update automatically.

Dapper not updating records in MySQL with one to one relationship

Goal
I intend to update records from WPF application to MySQL.
The is the loaded data:
User is able to edit the data:
On button click, the data should update:
It detects the changes from the UI and shows updated records.
Problem
When looking in my database, I see the column Name has changed but PositionId remained the same:
Why is PositionId not updating? And how can I update it?
Code
Models
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public Position Position { get; set; }
}
public class Position
{
public int PositionId { get; set; }
public string PositionTitle { get; set; }
public override bool Equals(object obj)
{
return obj is Position p && PositionId == p.PositionId;
}
public override int GetHashCode() => PositionId.GetHashCode();
}
View Model
public class MainViewModel : INotifyPropertyChanged
{
private ObservableCollection<Person> people;
public ObservableCollection<Person> People
{
get { return people; }
set
{
people = value;
OnPropertyChanged();
}
}
private ObservableCollection<Position> _positions;
public ObservableCollection<Position> Positions
{
get { return _positions; }
set
{
_positions = value;
OnPropertyChanged();
}
}
private static string connString = "..Connection String Goes here";
private List<Person> LoadPersonData()
{
string query = "SELECT PersonId, Name, b.PositionId, b.PositionTitle FROM Person a JOIN Position b ON a.PositionId = b.PositionId";
using (MySqlConnection conn = new MySqlConnection(connString))
{
var details = conn.Query<Person, Position, Person>(query, (person, position) =>
{
person.Position = position;
return person;
}, splitOn: "PositionId").ToList();
return details;
}
}
private List<Position> LoadPositionsData()
{
string query = "SELECT PositionId, PositionTitle FROM Position";
using (MySqlConnection conn = new MySqlConnection(connString))
{
var details = conn.Query<Position>(query).ToList();
return details;
}
}
public MainViewModel()
{
People = new ObservableCollection<Person>();
Positions = new ObservableCollection<Position>();
// Add each record to property named People
LoadPersonData().ForEach(record => People.Add(record));
// Add each record to peroperty named Positions
LoadPositionsData().ForEach(record => Positions.Add(record));
Command = new RelayCommand(param => EditData());
}
public ICommand Command { get; }
private void EditData()
{
// Update records in MySQL using dapper
DapperPlusManager.Entity<Person>().Table("Person").Identity(x => x.PersonId);
DapperPlusManager.Entity<Position>().Table("Position").Identity(x => x.PositionId);
using(MySqlConnection conn = new MySqlConnection(connString))
{
conn.BulkUpdate(People, x => x.Position);
}
}
#region Prop Changed
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
View
<StackPanel>
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTemplateColumn Header="Position Title">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Positions,
RelativeSource={RelativeSource AncestorType=DataGrid}}"
DisplayMemberPath="PositionTitle"
SelectedValue="{Binding Path=Position, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Content="Save new data" Command="{Binding Command}" />
</StackPanel>
I think I've got it. What I noticed is in the documentation example:
This loop
So I changed my EditData method to the following code:
private void EditData()
{
foreach(var item in People)
{
item.PositionId = item.Position.PositionId;
}
// Update records in MySQL using dapper
DapperPlusManager.Entity<Person>().Table("Person").Identity(x => x.PersonId);
DapperPlusManager.Entity<Position>().Table("Position").Identity(x => x.PositionId);
using(MySqlConnection conn = new MySqlConnection(connString))
{
conn.BulkUpdate(People, x => x.Position);
}
}
and also added a Property in the Person model
public int PositionId { get; set; }

Get updated value for a specific DataGridTemplateColumn in WPF

Goal
I am aiming to alter the selected value for a record and get the new value for a specific column in a DataGrid.
Right now, If I was to change a value in the Name column:
It detects the change:
Problem
When I change the position title, it does not show the new value.
Question
Why does it not detect the new value? And how do I do it?
What I have tried
I have tried to add OnPropertyChanged to all properties (except the override) for both models. This didn't do anything.
Code
Models
public class Person
{
public string Name { get; set; }
public Position Position { get; set; }
}
public class Position
{
public int PositionId { get; set; }
public string PositionTitle { get; set; }
public override bool Equals(object obj) =>
obj is Position p && PositionId == p.PositionId;
public override int GetHashCode() => PositionId.GetHashCode();
}
ViewModel
public class MainViewModel : INotifyPropertyChanged
{
private ObservableCollection<Person> people;
public ObservableCollection<Person> People
{
get { return people; }
set
{
people = value;
OnPropertyChanged();
}
}
private ObservableCollection<Position> _positions;
public ObservableCollection<Position> Positions
{
get { return _positions; }
set
{
_positions = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
People = new ObservableCollection<Person>();
People.Add(new Person { Name = "Name 1", Position = new Position { PositionId = 1, PositionTitle = "Position Title 1" } });
People.Add(new Person { Name = "Name 2", Position = new Position { PositionId = 1, PositionTitle = "Position Title 1" } });
People.Add(new Person { Name = "Name 3", Position = new Position { PositionId = 2, PositionTitle = "Position Title 2" } });
Positions = new ObservableCollection<Position>();
Positions.Add(new Position { PositionId = 1, PositionTitle = "Position Title 1" });
Positions.Add(new Position { PositionId = 2, PositionTitle = "Position Title 2" });
Command = new RelayCommand(param => EditData());
}
public ICommand Command { get; }
private void EditData()
{
var newData = People;
}
#region Prop Changed
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
View
<DataTemplate DataType="{x:Type local:MainViewModel}">
<StackPanel>
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTemplateColumn Header="Position Title">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Positions,
RelativeSource={RelativeSource AncestorType=DataGrid}}"
DisplayMemberPath="PositionTitle"
SelectedValue="{Binding Path=Position}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Content="Save new data" Command="{Binding Command}" />
</StackPanel>
</DataTemplate>
Set the UpdateSourceTrigger of the SelectedValue binding to PropertyChanged:
SelectedValue="{Binding Path=Position, UpdateSourceTrigger=PropertyChanged}"

Having trouble adding items to a WPF ListView

I've got a simple application connected to a SQL Server database, and I'm trying to pull data from the server to add it to a WPF ListView. I've done what I thought was the hard part of that, and successfully pulled the data - confirmed that already. But when I try to add it to my existing listview, the listview remains blank. Here's the code I've got to add it.
GetEmployees uses a middle tier class to connect to the database and retrieve the properties of an Employee. It successfully creates all of these variables and assigns them values from the database. The bottom line - lvwEmpSearch.Items.Add(emp); is what does not work.
Edit: Code is adjusted.
Code for adding to the listview:
public ObservableCollection<EmployeeViewModel> Employees { get; set; } = new ObservableCollection<EmployeeViewModel>();
private void btnFindAllEmployees_Click(object sender, RoutedEventArgs e)
{
List<Employee> empList = GetEmployees();
foreach (Employee emp in empList)
{
var model = new EmployeeViewModel
{
empID = emp._empID,
Name = emp._fName + " " + emp._lName,
Address = emp._address + ", " + emp._city + ", " + emp._state + " " + emp._zip,
HireDate = emp._doH,
Phone = emp._phone,
PayRate = emp._payRate,
Email = emp._email,
};
Employees.Add(model);
}
}
EmployeeViewModel class:
public class EmployeeViewModel
{
public int empID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public DateTime HireDate { get; set; }
public string Phone { get; set; }
public decimal PayRate { get; set; }
public string Email { get; set; }
}
Create a ViewModel to hold the item information you want displayed.
Based on column binding you are looking for something like
public class EmployeeViewModel {
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public DateTime HireDate { get; set; }
public string Phone { get; set; }
public decimal PayRate { get; set; }
public string Email { get; set; }
}
Create an observable collection to hold the items
public ObservableCollection<EmployeeViewModel> Employees { get; set; } = new ObservableCollection<EmployeeViewModel>();
and to allow the view to bind to the list
<ListView x:Name="lvwEmpSearch" ItemsSource="{Binding Employees}" >
<!-- ...removed for brevity -->
</ListView>
Now all that is needed is to populate the collection within the main ViewModel
List<Employee> empList = GetEmployees();
foreach (Employee emp in empList) {
var model = new EmployeeViewModel {
ID = emp._empID,
Name = emp._fName + " " + emp._lName,
Address = emp._address + ", " + emp._city + ", " + emp._state + " " + emp._zip,
HireDate = emp._doH,
Phone = emp._phone,
PayRate = emp._payRate,
Email = emp._email,
};
Employees.Add(model);
}
You seem to be doing everything in the code behind so you would need to bind the view. The initial assumption was that you were following the MVVM pattern.
//CTOR
public EmployeesView() {
this.InitializeComponents();
this.Employees = new ObservableCollection<EmployeeViewModel>();
//Bind the view so that
this.DataContext = this;
}
public ObservableCollection<EmployeeViewModel> Employees { get; private set; }
private void btnFindAllEmployees_Click(object sender, RoutedEventArgs e) {
//...code removed for brevity
}
Try doing the following:
In your view:
<ListView ItemsSource="{Binding Employees}">
<ListView.View>
<GridView>
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}"></GridViewColumn>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
In your code behind of the view:
public MainWindow() {
InitializeComponent();
DataContext = new MainViewModel();
}
The MainViewModel code:
public class MainViewModel : INotifyPropertyChanged {
public MainViewModel() {
FEmployees = new ObservableCollection<Employee>();
FEmployees.Add(new Employee {ID = 1,Name="Jordy van Eijk"});
FEmployees.Add(new Employee {ID = 2,Name="John Doe"});
FEmployees.Add(new Employee {ID = 3,Name="Jane Doe"});
}
private ObservableCollection<Employee> FEmployees;
public ObservableCollection<Employee> Employees {
get { return FEmployees; }
set {
FEmployees = value;
OnPropertyChanged(nameof(Employees));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string APropertyName = null) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(APropertyName));
}
}
public class Employee {
public int ID { get; set; }
public string Name { get; set; }
}
Youshould be able to create an ObservableCollection containing your employees and bind them to the ItemsSource.
private ObservableCollection<Employee> _employees = new ObservableCollection<Employee>();
public ObservableCollection<Employee> Employees
{
get { return _employees; }
protected set
{
if (_employees == value)
return;
_employees = value;
OnPropertyChanged("Employees");
}
}
And bind it like this:
<ListView ItemsSource="{Binding Employees}"/>
And in code you could fill it with:
Employees.AddRange(GetEmployees());

WPF Binding GridView to Element in collection

I'm creating a form that will allows user to add filters to data for processing.
I have setup:
public Class RuleGroup
{
public ObservableCollection<Rule> Rules {get; set;}
...
}
public Class Rule
{
public ObservableCollection<String> Fields {get; set;}
public ObservableCollection<Rule> Rules {get; set;}
...
}
public class Criteria
{
public int ItemId{ get; set;}
public string Field{ get; set;}
public OperationType Operation{ get; set;}
public string Value {get; set;}
public string Value2 {get; set;}
}
So a Rule has a List of Criteria that must be matched if the rule is to be applied. Each Criteria in a Rule must specify a value for every field selected. The Amount of fields may vary from One RuleGroup to the next.
I am trying to set up a form that is user friendly when creating multiple Rules. I was thinking of having a GridView on the form that is some how bound to this class layout.
Rule = Row
Criteria = Column
Currently I have function that generates a DataTable based on the Rules/Criteria as the user move from one RuleGroup to the next, but I think there my be an nicer solution to this
Any ideas or help would be much appreciated
Thanks
Right Think I have got it.
Needed to change my Classes around a bit to get the correct groups / hierarchy. I have then been able to bind the column using the items index in the collection.
This has given me the outcome I wanted, Though there is a minor issue where I would like to be able to access the index using the string Name rather then the position. I am currently having to make sure that the "Criterion" are in the correct order when accessing the values.
Here is a link to the Source code
Rule Group
public class RuleGroup
{
public String Description { get; set; }
public ObservableCollection<Rule> Rules { get; set; }
public RuleGroup()
{
Rules = new ObservableCollection<Rule>();
}
}
Rule
public class Rule
{
public Rule()
{
Criteria = new ObservableCollection<Criteria>();
}
public String Description { get; set; }
public ObservableCollection<Criteria> Criteria { get; set; }
readonly ObservableCollection<RuleField> _Fields = new ObservableCollection<RuleField>();
public IEnumerable<RuleField> Fields
{
get
{
return _Fields;
}
}
public void AddField(string name, string header)
{
if (_Fields.FirstOrDefault(i => i.Name == name) == null)
{
RuleField field = new RuleField(_Fields.Count)
{
Name = name,
Header = header
};
_Fields.Add(field);
AddFieldToCriteria(field);
}
}
void AddFieldToCriteria(RuleField field)
{
foreach (Criteria c in Criteria)
{
if (c.Values.FirstOrDefault(i => i.Field == field) == null)
c.Values.Add(new Criterion() { Field = field, Operation = 1 });
}
}
}
Criteria
public class Criteria
{
public Criteria()
{
Values = new ObservableCollection<Criterion>();
}
public ObservableCollection<Criterion> Values { get; set; }
public Criterion this[int index]
{
get
{
return Values.OrderBy(i=>i.Field.Position).ElementAt(index);
}
set
{
Criterion c = Values.OrderBy(i => i.Field.Position).ElementAt(index);
c= value;
}
}
}
Criterion
public class Criterion
{
public RuleField Field { get; set; }
public int Operation { get; set; }
public object Value { get; set; }
public object Value2 { get; set; }
}
RuleField
public class RuleField
{
public string Name { get; set; }
public string Header { get; set; }
int _Position = 0;
public int Position
{
get
{
return _Position;
}
}
public RuleField(int position)
{
_Position = position;
}
}
View Model
public delegate void UpdateColumnsEventHandler(object sender, UpdateColumnsEventArgs e);
public class UpdateColumnsEventArgs
{
public IEnumerable<RuleField> Columns { get; set; }
public UpdateColumnsEventArgs()
{
Columns = new List<RuleField>();
}
public UpdateColumnsEventArgs(IEnumerable<RuleField> columns)
{
Columns = columns;
}
}
public class MainWindowViewModel
{
public event UpdateColumnsEventHandler UpdateColumns;
public ObservableCollection<RuleGroup> RuleGroups { get; set; }
RuleGroup _SelectedRuleGroup = null;
public RuleGroup SelectedRuleGroup
{
get
{
return _SelectedRuleGroup;
}
set
{
if (_SelectedRuleGroup == value)
return;
_SelectedRuleGroup = value;
}
}
public Rule _SelectedRule = null;
public Rule SelectedRule
{
get
{
return _SelectedRule;
}
set
{
if (_SelectedRule == value)
return;
_SelectedRule = value;
if (UpdateColumns != null)
UpdateColumns(this, new UpdateColumnsEventArgs(_SelectedRule.Fields));
}
}
public MainWindowViewModel()
{
RuleGroups = new ObservableCollection<RuleGroup>();
RuleGroup rg = new RuleGroup();
rg.Description = "Rule Group A";
Rule r = new Rule();
r.Description = "Rule 1";
Random random = new Random();
int range = 10000;
for (int x = 0; x < 2000; x++)
{
Criteria c = new Criteria();
c.Values.Add(new Criterion()
{
Field = new RuleField(0)
{
Name = "A",
Header = "A Column"
},
Operation = 1,
Value = "X"
});
c.Values.Add(new Criterion()
{
Field = new RuleField(0)
{
Name = "B",
Header = "B Column"
},
Operation = 1,
Value = x % 10
});
r.Criteria.Add(c);
}
#region Fields
r.AddField("A", "A Column");
r.AddField("B", "B Column");
r.AddField("C", "C Column");
#endregion
rg.Rules.Add(r);
r = new Rule();
r.Description = "Rule 2";
for (int x = 0; x < 1750; x++)
{
r.Criteria.Add(new Criteria());
}
#region Fields
r.AddField("A", "A Column");
r.AddField("B", "B Column");
#endregion
rg.Rules.Add(r);
RuleGroups.Add(rg);
}
}
WPF Window
<Window x:Class="RuleMappingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:RuleMappingTest"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:MainWindowViewModel UpdateColumns="UpdateGridColumns"/>
</Window.DataContext>
<Grid Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding RuleGroups}" SelectedItem="{Binding SelectedRuleGroup}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Grid.Row="1" ItemsSource="{Binding SelectedRuleGroup.Rules}" SelectedItem="{Binding SelectedRule}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<DataGrid x:Name="CriteriaGrid" Grid.Row="2" ItemsSource="{Binding SelectedRule.Criteria}" AutoGenerateColumns="False" >
</DataGrid>
</Grid>
</Window>
WPF Code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void UpdateGridColumns(object sender, UpdateColumnsEventArgs e)
{
CriteriaGrid.Columns.Clear();
foreach (RuleField rf in e.Columns)
{
DataGridTextColumn c = new DataGridTextColumn();
c.Header = rf.Header;
Binding b = new Binding(String.Format("[{0}].Value", rf.Position));
CriteriaGrid.Columns.Add(c);
c.Binding = b;
}
}
}

Categories