Implementing IsDirty to properties on my Model (WPF MVVM) - c#

I have an observable collection of Suppliers that I want to load into a gridview and then have users edit any relevant information on the supplier. My issue is I'm not sure how to implement an IsDirty field for each property on the supplier (Model) that can be changed. I have the IsDirty bits created as such
#region SupplierID
private int _SupplierID;
public int SupplierID
{
get
{
return _SupplierID;
}
set
{
if (_SupplierID != value)
{
_SupplierID = value;
OnPropertyChanged("SupplierID");
}
}
}
#endregion
#region Address
private string _Address;
public string Address
{
get
{
return _Address;
}
set
{
if (_Address != value)
{
_Address = value;
IsDirtyAddress = true;
OnPropertyChanged("Address");
}
}
}
public bool IsDirtyAddress{ get; set; }
#endregion
#region City
private string _City;
public string City
{
get
{
return _City;
}
set
{
if (_City != value)
{
_City = value;
IsDirtyCity = true;
OnPropertyChanged("City");
}
}
}
public bool IsDirtyCity { get; set; }
#endregion
#region State
private string _State;
public string State
{
get
{
return _State;
}
set
{
if (_State != value)
{
_State = value;
IsDirtyState = true;
OnPropertyChanged("State");
}
}
}
public bool IsDirtyState { get; set; }
#endregion
#region Zip
private string _Zip;
public string Zip
{
get
{
return _Zip;
}
set
{
if (_Zip != value)
{
_Zip = value;
IsDirtyZip = true;
OnPropertyChanged("Zip");
}
}
}
public bool IsDirtyZip { get; set; }
#endregion
The problem is that when I build the list of suppliers (ViewModel), I actually end up setting the IsDirty bits to true. What is the best way to set my Address, City, State, Zip when creating the supplier without setting the IsDirty bits to true. Do I need an initialization function in my Model?
for (int i = 0; i < dtSupplier.Rows.Count; i++)
{
Supplier s = new Supplier()
{
SupplierID = Convert.ToInt32(dtSupplier.Rows[i]["SupplierID"].ToString()),
Address = dtSupplier.Rows[i]["Address"].ToString(),
City = dtSupplier.Rows[i]["City"].ToString(),
State = dtSupplier.Rows[i]["State"].ToString(),
Zip = dtSupplier.Rows[i]["Zip"].ToString()
};
Suppliers.Add(s);
}
Maybe I'm going about the whole IsDirty approach the wrong way. I just want to know which values actually changed so my SQL update statement will only include the changed values when a user saves. Thanks!

You need to do a few things:
Add a flag to your ViewModel and name it Loading. When you are loading the ViewModel, set the Loading property to true. When finished loading, set it to false.
Pass your model to your ViewModel but do not expose it. Simply store it in your ViewModel.
When the property is set, check if the ViewModel is in state Loading and do not set IsDirty flags. Also, even if not in loading state, compare the values to the value in your model and see if they are the same.
Do not use hardcoded strings because it is easy to make a mistake. Use nameof (see my example below).
Do not let other people from outside set the IsDirty flag so make the setter private.
I am pretty sure there are libraries that do this already but I do not know of any so perhaps someone else can chime in.
Here is a hypothetical example:
public class Model
{
public string Name { get; set; }
}
public class ViewModel : INotifyPropertyChanged
{
private readonly Model model;
public ViewModel(Model model)
{
this.model = model;
}
public bool Loading { get; set; }
public bool IsDirtyName { get; private set; }
private string name;
public string Name
{
get
{
return this.name;
}
set
{
if (this.Loading)
{
this.name = value;
return;
}
if (this.model.Name != value)
{
IsDirtyName = true;
OnPropertyChanged(nameof(Name));
}
}
}
private void OnPropertyChanged(string propertyName)
{
// ...
}
public event PropertyChangedEventHandler PropertyChanged;
}
If you pay attention the above design, you do not even need all those IsDirty flags and IsLoading etc. You can actually just have one method in your ViewModel that you call during saving and ask it to check and return all the properties that have changed. The ViewModel will compare its own properties against the Model properties and return a dictionary. There are many ways do achieve what you want.

One option is to handle the IsDirty logic on a different class which will store the original values of the Supplier object instance. You can then use that class to GetChangedPropertyNames or check if your object HasChanges.
class Supplier
{
private string _Address;
public string Address
{
get
{
return _Address;
}
set
{
if (_Address != value)
{
_Address = value;
OnPropertyChanged("Address");
}
}
}
}
class SupplierIsDirtyTracker
{
private Dictionary<string, object> _originalPropertyValues = new Dictionary<string, object>();
private Supplier _supplier;
public void Track(Supplier supplier)
{
_supplier = supplier;
_originalPropertyValues.Add(nameof(Supplier.Address), supplier.Address);
}
public bool HasChanges()
{
return !Equals(_originalPropertyValues[nameof(Supplier.Address)], _supplier.Address);
}
public IEnumerable<string> GetChangedPropertyNames()
{
if(!Equals(_originalPropertyValues[nameof(Supplier.Address)], _supplier.Address))
{
yield return nameof(Supplier.Address);
}
}
}
You can also use Reflection on your IsDirtyTracker class to eliminate hardcoding the property names.

Related

How to validate child objects by implementing IDataErrorInfo on parent class and how to bind validation to objects

I want to ask exactly the same question that was asked in this link and also a person answered too but I couldn't understand how to incorporate that in the code. Sorry for asking the noob question, please bear with me.
the link for the question is How to validate child objects by implementing IDataErrorInfo on parent class.
Question is exactly the same and answer is how to achieve but what I find lacking is how to use that class for validation in Employee class.
I'm posting the same question below:
I have two model classes. Parent class has an object of another (child) class as its property. (i mean nested objects and not inherited objects)
public class AddEditItemVM
{
#region Properties/Fields
//validate here: Category cannot be empty, it has to be selected
public Category SelectedCategory
{
get => _SelectedCategory;
set
{
_SelectedCategory = value;
OnPropertyChanged(nameof(SelectedCategory));
}
}
private Category _SelectedCategory = new CCategory();
public Items Source
{
get => _Source;
set
{
_Source = value;
OnPropertyChanged(nameof(Source));
}
}
private Items _Source = new Items();
#endregion Properties/Fields
}
Category class is
public class Category
{
#region Properties/Fields
public long Id
{
get => _Id;
set
{
_Id = value;
OnPropertyChanged(nameof(Id));
}
}
long _Id;
public string Name
{
get => _Name;
set
{
_Name = value;
OnPropertyChanged(nameof(Name));
}
}
private string _Name;
#endregion Properties/Fields
}
Items class is
public class Items
{
#region Properties/Fields
public int CategoryID
{
get => _CategoryID;
set
{
_CategoryID = value;
OnPropertyChanged(nameof(CategoryID));
}
}
int _CategoryID;
//I want to validate this, item name cannot be empty and cannot greater than 20 characters
public string Name
{
get => _Name;
set
{
_Name = value;
OnPropertyChanged(nameof(Name));
}
}
string _Name = String.Empty;
//validate for percentage like percentage should be between 1 and 100
public decimal? TaxPerc
{
get => _TaxPerc;
set
{
//_TaxPerc = value;
_TaxPerc = Properties.Settings.Default.GSTPerc;
OnPropertyChanged(nameof(TaxPerc));
OnPropertyChanged(nameof(PricePlsTax));
OnPropertyChanged(nameof(TaxAmount));
}
}
decimal? _TaxPerc = null;
//price cannot be 0
//* required field!
public decimal? Price
{
get => _Price;
set
{
_Price = value;
OnPropertyChanged(nameof(Price));
OnPropertyChanged(nameof(PricePlsTax));
OnPropertyChanged(nameof(TaxAmount));
}
}
decimal? _Price = null;
#endregion Properties/Fields
}
and the person answered with validation with IDataErrorInfo (which is what I want)
public abstract class DataErrorInfo : IDataErrorInfo
{
string IDataErrorInfo.Error
{
get { return string.Empty; }
}
string IDataErrorInfo.this[string columnName]
{
get
{
var prop = this.GetType().GetProperty(columnName);
return this.GetErrorInfo(prop);
}
}
private string GetErrorInfo(PropertyInfo prop)
{
var validator = this.GetPropertyValidator(prop);
if (validator != null)
{
var results = validator.Validate(this);
if (!results.IsValid)
{
return string.Join(" ",
results.Select(r => r.Message).ToArray());
}
}
return string.Empty;
}
private Validator GetPropertyValidator(PropertyInfo prop)
{
string ruleset = string.Empty;
var source = ValidationSpecificationSource.All;
var builder = new ReflectionMemberValueAccessBuilder();
return PropertyValidationFactory.GetPropertyValidator(
this.GetType(), prop, ruleset, source, builder);
}
}
but I couldn't understand the final part i.e. implementation I mean how to use this class in the implemented class. He said to implement it by making it the parent class which is as below
public AddEditItemVM : DataErrorInfo
{
}
but I wanna know how to bind the validation with the objects.
<TextBox Text="{Binding SelectedCategory.Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"></TextBox>
This will bind to your name property of your selectedcategory class and show if there is any validation errors.

Neo4jClient Node/Relationship Class conventions

Is there a standard naming convention for the properties/methods of a node/relationship class when working with Neo4jClient?
I'm following this link Neo4jClient - Retrieving relationship from Cypher query to create my relationship class
However, there are certain properties of my relationship which i can't get any value despite the relationship having it. While debugging my code, i realized certain properties was not retrieved from the relationship when creating the relationship object.
this is my relationship class
public class Creates
{
private string _raw;
private int _sourcePort;
private string _image;
private int _DestinationPort;
private int _eventcode;
private string _name;
private string _src_ip;
private int _src_port;
private string _dvc;
private int _signature_ID;
private string _dest_ip;
private string _computer;
private string _sourceType;
private int _recordID;
private int _processID;
private DateTime _time;
private int _dest_port;
public string Raw { get { return _raw; } set { _raw = value; } }
public int SourcePort { get { return _sourcePort; } set { _sourcePort = value; } }
public string Image { get { return _image; } set { _image = value; } }
public int DestinationPort { get { return _DestinationPort; } set { _DestinationPort = value; } }
public int Eventcode { get { return _eventcode; } set { _eventcode = value; } }
public string Name { get { return _name; } set { _name = value; } }
public string Src_ip { get { return _src_ip; } set { _src_ip = value; } }
public int Src_port { get { return _src_port; } set { _src_port = value; } }
public string DVC { get { return _dvc; } set { _dvc = value; } }
public int Signature_ID { get { return _signature_ID; } set { _signature_ID = value; } }
public string Dest_ip { get { return _dest_ip; } set { _dest_ip = value; } }
public string Computer { get { return _computer; } set { _computer = value; } }
public string SourceType { get { return _sourceType; } set { _sourceType = value; } }
public int RecordID { get { return _recordID; } set { _recordID = value; } }
public int ProcessID { get { return _processID; } set { _processID = value; } }
public DateTime Indextime { get { return _time; } set { _time = value; } }
public int Dest_port { get { return _dest_port; } set { _dest_port = value; } }
}
This is another class
public class ProcessConnectedIP
{
public Neo4jClient.RelationshipInstance<Pivot> bindto { get; set; }
public Neo4jClient.Node<LogEvent> bindip { get; set; }
public Neo4jClient.RelationshipInstance<Pivot> connectto { get; set; }
public Neo4jClient.Node<LogEvent> connectip { get; set; }
}
This is my neo4jclient query to get the relationship object
public IEnumerable<ProcessConnectedIP> GetConnectedIPs(string nodeName)
{
try
{
var result =
this.client.Cypher.Match("(sourceNode:Process{name:{nameParam}})-[b:Bind_IP]->(bind:IP_Address)-[c:Connect_IP]->(connect:IP_Address)")
.WithParam("nameParam", nodeName)
.Where("b.dest_ip = c.dest_ip")
.AndWhere("c.Image=~{imageParam}")
.WithParam("imageParam", $".*" + nodeName + ".*")
.Return((b, bind, c, connect) => new ProcessConnectedIP
{
bindto = b.As<RelationshipInstance<Creates>>(),
bindip = bind.As<Node<LogEvent>>(),
connectto = c.As<RelationshipInstance<Creates>>(),
connectip = connect.As<Node<LogEvent>>()
})
.Results;
return result;
}catch(Exception ex)
{
Console.WriteLine("GetConnectedIPs: Error Msg: " + ex.Message);
return null;
}
}
This is the method to read the results
public void MyMethod(string name)
{
IEnumerable<ProcessConnectedIP> result = clientDAL.GetConnectedIPs(name);
if(result != null)
{
var results = result.ToList();
Console.WriteLine(results.Count());
foreach (ProcessConnectedIP item in results)
{
Console.WriteLine(item.Data.Src_ip);
Console.WriteLine(item.bindto.StartNodeReference.Id);
Console.WriteLine(item.bindto.EndNodeReference.Id);
Console.WriteLine(item.connectto.StartNodeReference.Id);
Console.WriteLine(item.connectto.EndNodeReference.Id);
Node<LogEvent> ans = item.bindip;
LogEvent log = ans.Data;
Console.WriteLine(log.Name);
Node<LogEvent> ans1 = item.connectip;
LogEvent log1 = ans1.Data;
Console.WriteLine(log1.Name);
}
}
}
Somehow, i'm only able to populate the relationship object with src_ip/src_port/dest_ip/dest_port values. the rest are empty.
Is there any possible reason why? I've played with upper/lower cases on the properties names but it does not seem to work.
This is the section of the graph im working with
This is the relationship properties sample:
_raw: Some XML dataSourcePort: 49767Image: C:\Windows\explorer.exeDestinationPort: 443EventCode: 3Name: Bind
IPsrc_ip: 172.10.10.104dvc: COMPUTER-NAMEsrc_port:
49767signature_id: 3dest_ip: 172.10.10.11Computer:
COMPUTRE-NAME_sourcetype:
XmlWinEventLog:Microsoft-Windows-Sysmon/OperationalRecordID:
13405621ProcessId: 7184_time: 2017-08-28T15:15:39+08:00dest_port: 443
I'm not entirely sure how your Creates class is ever populated, in particular those fields - as your Src_port property doesn't match the src_port in the sample you provided (case wise).
I think it's probably best to go back to a super simple version. Neo4jClient will map your properties to the properties in the Relationship as long as they have the same name (and it is case-sensitive).
So start with a new Creates class (and use auto properties - it'll make your life a lot easier!)
public class Creates
{
public string Computer { get; set; }
}
Run your query with that and see if you get a result, then keep on adding properties that match the name and type you expect to get back (int, string etc)
It seems that i have to give neo4j node/relationship property names in lowercase and without special characters at the start of the property name, in order for the above codes to work.
The graph was not created by me at the start thus i had to work on it with what was given. I had to get the developer who created the graph to create the nodes with lowercases in order for the above to work.

ASP MVC 5 Creating Treeview with check boxes on Leafnode

This is my model structure from which I want to use to create a treeview, with checkboxes on leaf nodes:
public class CategoryEntity : BaseEntity
{
public CategoryEntity()
: base()
{
}
private Guid _categoryId;
public Guid CategoryId
{
get { return _categoryId; }
set { _categoryId = value; InvokePropertyChanged("CategoryId"); IsDirty = true; }
}
private string _categoryname;
public string CategoryName
{
get { return _categoryname; }
set { _categoryname = value; InvokePropertyChanged("CategoryName"); IsDirty = true; }
}
private string _categorydescription;
public string CategoryDescription
{
get { return _categorydescription; }
set { _categorydescription = value; InvokePropertyChanged("CategoryDescription"); IsDirty = true; }
}
private string _categorytype;
public string CategoryType
{
get { return _categorytype; }
set { _categorytype = value; InvokePropertyChanged("CategoryType"); IsDirty = true; }
}
private Guid? _parentcategoryId;
public Guid? ParentCategoryId
{
get { return _parentcategoryId; }
set { _parentcategoryId = value; InvokePropertyChanged("ParentCategoryId"); IsDirty = true; }
}
}
The problem is that I am new to MVC and I don't know which controls to use to display this values in a tree structure. Can anyone suggest a way of doing this?

How to detect whether the Property value(ReferenceType property) is changed in c#?

I have a Customer class, I would like to get notified when the user has changed the value of the Customer.CityInfo property.
public class City
{
public long Id {get;set;}
public string Code {get;set;}
}
public class Customer
{
private City cityInfo;
private string name;
public long Id { get; set; }
public bool IsCityModified { get; set;}
public bool IsCustomerNameModified { get; set; }
public string Name
{
get{ return name;}
set
{
if(name!=value)
{
IsCustomerNameModified=true; }name=value;
}
}
}
public City CityInfo
{
get
{
if(cityInfo==null)
{
cityInfo=new City();
}
return cityInfo;
}
set{
if(this.cityInfo!=value)
{
IsCityModified =true;
}
this.cityInfo=value;
}
}
}
public ActionResult Save()
{
Customer customer=this.currentCustomerSession;
if(TryUpdateModel<Customer>(customer)){
UpdateModel<Customer>(customer)
}
if(customer.IsCustomerNameModified ){
//I am able to detect whether the customerName value has been changed in the frontend.
}
if(customer.IsCityModified){
//I am not able to detect whether the city value has been changed in the frontend.
}
}
I could set the flag(IsCustomerNameModified) to true if the Customer Name changed since its a value type. But Not able to detect the changes done in Reference Types.
Could anyone help?
This kind of issue is usually handled via a change notification system. See this article: How to: Implement Property Change Notification
Snippet:
public string PersonName
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("PersonName");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
Using this pattern will help you avoid the need to set flags or other such machinations.
Paul is correct you need to implement INotifyProperty change. Here is a quick example. It's very simple.
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _number;
public BaseViewModel()
{
PropertyChanged += (o, p) =>
{
//this is the place you would do what you wanted to do
//when the property has changed.
};
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
public int Number
{
get { return _number; }
set
{
_number = value;
OnPropertyChanged("Number");
}
}
}
I agree with Paul's answer but you may even need to override the Equals() and GetHashCode() functions in the City class. The overridden function can check to see if the Code and ID have changed.
public override bool Equals( object obj )
{
City other = obj as City;
if ( ( other != null ) && ( other.Id == this.Id ) && ( other.Code == this.Code ) )
{
return ( true );
}
return ( false );
}
public override int GetHashCode( )
{
return ( this.Id ^ this.Code.GetHashCode() ) ;
}
You could use INotifyPropertyChanged , but I guess you are not binding the object to some UI element.
In that case if it is required only to know if the CityInfo property is changed, the simplest
solution is to raise a custom event.
public class Customer
{
private City cityInfo;
private string name;
public long Id { get; set; }
public bool IsCityModified { get; set;}
public event Action<City> OnCityInfoChanged;
public bool IsCustomerNameModified { get; set; }
public string Name
{
get{ return name;}
set
{
if(name!=value)
{
IsCustomerNameModified=true; }name=value;
}
}
}
public City CityInfo
{
get
{
if(cityInfo==null)
{
cityInfo=new City();
}
return cityInfo;
}
set{
if(this.cityInfo ==value)
return;
IsCityModified =true;
this.cityInfo=value;
if(OnCityInfoChanged != null)
OnCityInfoChanged(value);
}
}
}
And
public ActionResult Save()
{
Customer customer=this.currentCustomerSession;
customer.OnCityInfoChanged += new Action<CityInfo>( (cityInfo) => {//Do Something with New CityInfo});
if(TryUpdateModel<Customer>(customer)){
UpdateModel<Customer>(customer)
}
if(customer.IsCustomerNameModified ){
//I am able to detect whether the customerName value has been changed in the frontend.
}
if(customer.IsCityModified){
//I am not able to detect whether the city value has been changed in the frontend.
}
}
Now that I correctly understood the problem, I would suggest to do the following:
Add a public bool Changed { get; private set; } property in your City object.
Then in each property of your object, check if the value has changed, and if it has, then put the Changed flag to true:
public int Id
{
{ get { return this.id } }
{
set
{
if (this.id != value) Changed = true;
this.id = value;
}
}
}
it is similar to implementing the IPropertyChanged interface, but this way you are sure you only get to check if the object was modified once.
It seems that the reference of your object doesn't change, so you only have to check it's Changed property (if the object's reference really changed, my precedent answer would have worked)

WPF Datagrid Databind to class with static properties and dictionary containing dynamic property value entries

UPDATED
I am updating this post because I did some more reading and decided to re-implement my solution.
Original Problem: I have a class with static properties and one Property that is a dynamic collection of properties (via a dictionary). I want to databind my class to a wpf datagrid where each static property should be a column and each dictionary entry should be a column in the grid.
After doing some more research, I decided to implement a PropertyBag class that will contain my Dictionary of properties and values. Almost everything is working now. I have my grid being displayed with all the correct columns and the static property values are being applied correctly.
However, now I am not able to get any of the values from the dictionary to be applied to the grid, and I am not sure where to go from here.
More info:
My database has 3 tables, a plate, a category, and a categoryplateassociation table. Each plate can have 0 to many categories. For now, I am populating each plate with all the categories and setting the strings to empty. Then, when an association is returned (between a plate and category), I am setting the real value on the specific category name. This all happens before the grid is created.
Property Bag:
public class PropertyBag
{
private readonly Dictionary<string, string> values = new Dictionary<string, string>();
public string this[string key]
{
get
{
string value;
values.TryGetValue(key, out value);
return value;
}
set
{
if (value == null) values.Remove(key);
else values[key] = value;
}
}
}
Revised Plate class
[TypeDescriptionProvider(typeof(PlateTypeDescriptionProvider))]
public class Plate : INotifyPropertyChanged
{
public int ID;
private string name;
private string status;
private string creator;
private Uri location;
private string description;
public Plate()
{
CustomCategories = new PropertyBag();
}
public PropertyBag CustomCategories { get; set; }
public string Name
{
get { return name;}
set
{
name = value;
NotifyPropertyChanged("Name");
}
}
public string Status
{
get { return status; }
set
{
status = value;
NotifyPropertyChanged("Status");
}
}
public string Creator
{
get { return creator; }
set
{
creator = value;
NotifyPropertyChanged("Creator");
}
}
public Uri Location
{
get { return location; }
set
{
location = value;
NotifyPropertyChanged("Location");
}
}
public string Description
{
get { return description; }
set
{
description = value;
NotifyPropertyChanged("Description");
}
}
public static Plate ConvertDataPlateToBusinessPlate(TestPlate dataPlate)
{
var plate = new Plate
{
Name = dataPlate.Name,
Status = dataPlate.Status,
Creator = dataPlate.Creator,
Description = dataPlate.Description,
Location = new Uri(dataPlate.Location)
};
return plate;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Revised CustomTypeDescriptor:
public override PropertyDescriptorCollection GetProperties()
{
return GetProperties(null);
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = new ArrayList();
foreach (PropertyDescriptor propertyDescriptor in base.GetProperties(attributes))
{
if(propertyDescriptor.PropertyType.Equals(typeof(PropertyBag)))
{
//Static list of all category names
var categoryNames = Categories.GetAll();
foreach (var categoryName in categoryNames)
{
properties.Add(new PropertyBagPropertyDescriptor(categoryName));
}
}
else
{
properties.Add(propertyDescriptor);
}
}
var props = (PropertyDescriptor[])properties.ToArray(typeof(PropertyDescriptor));
return new PropertyDescriptorCollection(props);
}
Revised PropertyDescriptor
public class PropertyBagPropertyDescriptor : PropertyDescriptor
{
public PropertyBagPropertyDescriptor(string name) : base(name, null)
{}
public override bool CanResetValue(object component)
{
return true;
}
public override object GetValue(object component)
{
return ((PropertyBag) component)[Name];
}
public override void ResetValue(object component)
{
((PropertyBag)component)[Name] = null;
}
public override void SetValue(object component, object value)
{
((PropertyBag) component)[Name] = (string) value;
}
public override bool ShouldSerializeValue(object component)
{
return ((PropertyBag)component)[Name] != null;
}
public override Type ComponentType
{
get { return typeof(PropertyBag); }
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type PropertyType
{
get { return typeof(string); }
}
}
simple ViewModel
public TestPlateAdministratorViewModel()
{
CommandAggregator = new TestPlateAdministratorCommandAggregator(this);
LoadData();
}
public static TestPlateAdministratorCommandAggregator CommandAggregator { get; set; }
public ObservableCollection<Plate> TestPlates{ get; set; }
private static void LoadData()
{
CommandAggregator.LoadPlatesCommand.Execute(null);
CommandAggregator.LoadCategoriesCommand.Execute(null);
}
}
does your Dictionary in your PropertyBag has a fixed size or does the keys are known?
you did not post your xaml for your datagrid, but the binding from the propertybag to one column could look like this:
<DataGridTextColumn Header="Col4TestKey" Binding="{Binding CustomCategories[test]}"/>
i really dont know wether your PropertyBag setter will work with binding. all in all this just would work if you have a know set of keys for your dictionary.
btw, i use flat datatables in my projects for such dynamic stuff, there are really easy to handle.

Categories

Resources