C# Databinding: Create object if null - c#

I have a report object (i.e. a business object) which has several dozen fields to populate. Each field by itself has INotifyPropertyChanged implemented. There is an accessor property for the active report called ActiveReport.
What I want to do is be able to Close the current report, without necessarily opening a new one, and be able to automatically create a report object when the user starts to enter data again.
Here is a rough idea of the structure. ActiveReport is the current report. The GUI is able to directly set the fields of the subclass (name/email) through binding. I want a new BusinessObject to be created when name is being set, but ActiveReport is null. One additional caveat, the report object is auto-generated from XSD files, so I'd rather not have to modify those.
class ControlClass {
public BusinessObject ActiveReport { get; set; }
}
class BusinessObject {
UserInfo field1 { get; set; }
}
class UserInfo : INotifyPropertyChanged {
DependencyProperty name;
DependencyProperty email;
}
I thought of the following scenarios:
Accessor property.
The binding does not seem to use the accessor.
Inserting a check into all event handlers.
I'd rather not have to resort to this -- this breaks the rationale behind using MVVM.
Multibinding
This would require the use of a converter class and instance, and that seems like overkill.
Converter
I thought to ask if there were any other good programming models for this in WPF.

You could create a behavior.
in it, you check if (AssociatedObject.DataContext as ReportObject) is null
and if it is, clear all your fields / set your datacontext / whatever

This should do the trick:
public class ControlClass
{
public BusinessObject ActiveReport { get; set; }
private UserInfo _editableUserData
public UserInfo EditableUserData
{
get { return _editableUserData; }
set
{
if (_editableUserData != null)
_editableUserData.PropertyChanged -= UserDataChanged;
_editableUserData = value;
if (_editableUserData != null)
_editableUserData.PropertyChanged += UserDataChanged;
RaisePropertyChanged("EditableUserData");
}
}
private void UserDataChanged(object sender, PropertyChangedEventArgs e)
{
if (ActiveReport == null)
ActiveReport = new BusinessObject(EditableUserData);
}
}

Related

MVVM Refresh Datagrid from ViewModel even when collection doesn't change

I'm writing an application to read and analyze some logs my company software uses. There are multiple types of logs, but let's take only two for the purpose of describing my problem. Namely, logs of TypeA and TypeB. I have designed one class to hold a single line of log data, named LogLine which looks like below.
public class LogLine
{
public long LineNum { get; set; }
public string Msg { get; set; }
}
So here's my problem/requirement.
In my main ViewModel, I'd like to read logs of each type only once when the application loads. Read TypeA logs one time, and store in an ObservableCollection of LogLine instances, do the same for TypeB. Then depending on my choice the DataGrid displays logs from one type, and if I click a button at any time, the same DataGrid should display logs from the other type. Note that my logs data doesn't change, I simply want to display my choice of logs.
For this I created three classes, namely, ControllerMain, ControllerA, and ControllerB. The last two derive from the former like so:
public class ControllerMain
{
public ControllerMain()
{
LogLineList = new ObservableCollection<LogLine>();
}
private ObservableCollection<LogLine> logLineList;
public ObservableCollection<LogLine> LogLineList
{
get { return logLineList; }
set { logLineList = value; }
}
}
public class ControllerA : ControllerMain
{
public ControllerA() { }
// More stuff here
}
public class ControllerB : ControllerMain
{
public ControllerB() { }
// More stuff here
}
As you can guess ControllerA is intended to hold logs of TypeA, and associated properties and methods unique to those logs. Same goes for TypeB logs.
In my ViewModel, I have instances of each of the classes above like so, and at application load I read log data and store in appropriate class object.
public ControllerMain COMMON_LOG { get; set; }
public ControllerA A_LOG { get; set; }
public ControllerB B_LOG { get; set; }
public ViewModelMain()
{
isAType = true;
ClickCommand = new CustomCommand(ClickCmd, CanClickCmd);
A_LOG = new ControllerA
{
// This simulates reading logs from files - done only once
LogLineList = DataService.GetAData()
};
B_LOG = new ControllerB
{
// This simulates reading logs from files - done only once
LogLineList = DataService.GetBData()
};
// This simulates switching to already loaded logs.
// When I do this the log lines don't change, but I want to refresh the datagrid and display correct info.
LoadAppropriateLog();
}
private void LoadAppropriateLog()
{
if (isAType)
{
COMMON_LOG = A_LOG;
isAType = false;
}
else
{
COMMON_LOG = B_LOG;
isAType = true;
}
}
My View binds to the COMMON_LOG instance like below:
<DataGrid Grid.Row="0" Margin="5"
Name="dgLogs"
AutoGenerateColumns="False" SelectionUnit="CellOrRowHeader"
ItemsSource="{Binding COMMON_LOG.LogLineList}">
Then at the click of a button, I call the above LoadAppropriateLog() method, so it will simply assign the instance of appropriate type to COMMON_LOG which is the instance I've used to data bind.
The problem is that when I do so, since the actual data in each instance's LogLineList doesn't change, the DataGrid doesn't automatically update to reflect my choice of logs.
Is there a way to manually refresh the DataGrid from my ViewModel after every time I switch the type of log?
If you'd like to run the project and see, here's a download link:
Download the VS Project
If you're binding to a property of a class in XAML, either
The property should never change its value after a binding would first see it, and should usually be readonly just to avoid mishaps -- or
The class should implement INotifyPropertyChanged and the property should raise PropertyChanged in its setter.
In your case, you're changing the value of COMMON_LOG, and you're never changing the value of its LogLineList.
tl;dr: So your main viewmodel needs to implement INotifyPropertyChanged, and raise PropertyChanged in the setter for COMMON_LOG. Anything that doesn't do those things isn't a viewmodel.
LogLineList being an ObservableCollection won't accomplish anything: What that class does is raise notifications when items are added, removed or replaced. That doesn't happen at any time after the binding sees it. Those instances of ObservableCollection don't even know that the main viewmodel even exists, so they certainly can't be expected to raise notification events when its properties change. Nor should they: Everybody is responsible for exactly his own notifications.
In fact, if you've made a design decision that those collections never change after initialization, use ReadOnlyCollection instead of ObservableCollection. Creating one is easy: Call List<T>.AsReadOnly<T>(). For any IEnumerable<T>, just call e.ToList().AsReadOnly(). ObservableCollection signals "you can add stuff to this". But nobody should. So don't give them ideas.

Updating UI when a model property changes in an ObservableCollection?

I have a view that has a group of images I get from a web service
I receive them in a list of this class:
public class ImageModel
{
public int Id { get; set; }
public string Name { get; set; }
public string imageUrl { get; set; }
}
under each image I show an up-vote button, so I added another bool property to the model above:
public bool UpVoted { get; set; }
the ListView that shows these images is bound to an ObservableCollection<ImageModel > , I want to change the voting icon through a converter that convert the value of UpVoted to the corresponding icon, when the user click the voting icon: a command execute this method:
private void OnVoting(ImageModel image)
{
Images.Single(x => x.id == image.id).UpVoted = !image.UpVoted;
}
the problem is that the UI is not updated, and to make sure that I understood the problem I turned the model to a View model and made the required changes to the UpVoted property (I'm using MVVM light library)
bool upVoted;
public bool UpVoted
{
get { return upVoted; }
set
{
Set(ref upVoted, value);
}
}
and it works now,
so I need to bind the UpVoted to the UI, so it's updated whenever it changed
first
your model class must inherit from MvxNotifyPropertyChanged
public class ImageModel : MvxNotifyPropertyChanged
{
public int Id { get; set; }
public string Name { get; set; }
private bool upVoted ;
public bool UpVoted
{
get { return upVoted ; }
set { upVoted = value; RaisePropertyChanged(() => UpVoted ); }
}
}
then with MvxValueConverter you ready to go
Mustafa's answer mentions a class that is specific to MvvmCross library.
Another alternative is TinyMvvm.
If you wish to write your own MVVM (or understand how MVVM works),
the general pattern is to implement INotifyPropertyChanged: Implement Property Change Notification, which I discuss here.
A convenient way to implement INotifyPropertyChanged, is to make a base class that does that implementation, then inherit from that base class. You can use the code in that sample as your base class. Or use a slightly different implementation, that avoids having to manually pass the property name as a string:
using System.ComponentModel;
using System.Runtime.CompilerServices;
// Use this as base class for all your "view model" classes.
// And possibly for your (domain) model classes.
// E.g.: "public class MyLoginViewModel : HasNotifyPropertyChanged".
// OR "public class MyLoginModel : HasNotifyPropertyChanged".
// Give it whatever name you want, for ViewModels I suggest "ViewModelBase".
public class HasNotifyPropertyChanged : INotifyPropertyChanged
{
// --- This is pattern to use to implement each property. ---
// This works for any property type: int, Color, etc.
// What's different from a standard c# property, is the "SetProperty" call.
// You will often write an IValueConverter (elsewhere) to use in XAML to convert from string to your property type,
// or from your property type to a type needed in your UI.
// Comment out this example property if you don't need it.
/// <summary>
/// Set to "true" at end of your initialization.
/// Then can use Property Trigger on Ready value=true in XAML to do something when your instance is ready for use.
/// For example, load something from web, then trigger to update UI.
/// </summary>
private bool _ready;
public bool Ready
{
get => _ready;
set => SetProperty(ref _ready, value);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
{
if (property == null || !property.Equals(value))
{
property = value;
RaisePropertyChanged(propertyName);
}
}
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Again, an alternative to the above code is to use an existing MVVM library.
For another alternative, that doesn't require writing "SetProperty(..)" or "OnPropertyChanged(..)" in all of your property setters, google for info about using Fody/PropertyChanged. Then you wouldn't need any of the above code; your class would simply inherit from INotifyPropertyChanged. (And in app startup, you call a method that "injects" the needed logic into all properties of all INotifyPropertyChanged classes.)
Acknowledgement: The code pattern in example above is based on one of the open source libraries. It might be from TinyMvvm.
you do not say which sort of container that you are using but not all controls are set to support two way notification by default. so you may have to add a
Mode=TwoWay
to get notifications from the back end that data has changed. Or as the previous answer by Mustafa indicated you may need to verify that your class is implementing the InotifyPropertyChanged event with mvvm light.

Sharing dependency property in C# (WPF) between two classes

I want two share a DepedencyProperty between to classes using AddOwner (any other approach is welcome), e.g.
class ClassA : DependencyObject
{
public int Number
{
get { return (int)GetValue(NumberProperty); }
set { SetValue(NumberProperty, value); }
}
public static readonly DependencyProperty NumberProperty =
DependencyProperty.Register("Number", typeof(int), typeof(ClassA),
new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.Inherits));
}
and
class ClassB : DependencyObject
{
public int Number
{
get { return (int)GetValue(NumberProperty); }
set { SetValue(NumberProperty, value); }
}
public static readonly DependencyProperty NumberProperty =
ClassA.NumberProperty.AddOwner(typeof(ClassB),
new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.Inherits));
}
like described here. As you might guess: Of course it doesn't work. That makes perfect sense, because it would make it impossible to create multiple instances of the same class that all have their "own" dependency property.
How do I make sure that all classes (and especially all instances) of ClassA, ClassB and any other class which refers to the property are talking about the exact same property (and therefore value)? A Singleton is no option, since Class A is a MainWindow and Class B is an UserControl (protected constructors are therefore not possible).
Regards,
Ben
I think you're misunderstanding the purpose of DependencyProperties.
They are basically a Property Definition, without a property Value.
They define things like name, type, default value, location of the value, etc however they do not contain the actual value itself. This allows the value to be provided with a binding pointing to any other property in any other location.
Your best bet is to probably just create a property that is backed by a singleton property.
public int Number
{
get { return MySingleton.Number; }
set { MySingleton.Number = value; }
}
Edit
Based on comments below where you say you want all instances of the object to respond to change notifications from any of the other objects, you'd want to implement INotifyPropertyChanged on your singleton object, and subscribe to it's PropertyChange event in each class that uses that value.
For example,
public ClassA
{
public ClassA()
{
MySingleton.PropertyChanged += Singleton_PropertyChanged;
}
void Singleton_PropertyChanged(object sender, NotifyPropertyChangedEventArgs e)
{
// if singleton's Number property changed, raise change
// notification for this class's Number property too
if (e.PropertyName == "Number")
OnPropertyChanged("Number");
}
public int Number
{
get { return MySingleton.Number; }
set { MySingleton.Number = value; }
}
}
One possible solution to what you want here is to use another class where you store that
value. e.g.
public class SomeValueStore : IValueStore
{
int myValue {get; set;}
}
Then, whereever you need that value, you can use Dependency injection to get it.
somewhere at Bootstrapper:
RootContainer.Register<IValueStore>(new SomeValueStore);
and in code:
var valueStore = RootContainer.Resolve<IValueStore();
valueStore.myValue = 42;
This is just an idea (And I know we have a ServiceLocator here).
Perhaps you can store a reference to that ValueStore somewhere where you
can get it from both classes you need it as a simple solution.
public SomeClassYouHaveAccessToFromBothSides
{
public IValueStore _store = new SomeValueStore();
}
Please excuse me. I do not have access to my repo / visual studio right now
so I cannot give better example. But I think the underlying idea is clear.

Customise the display of a auto-generated class

I have a DB and I created classes to access/manage it using SQLMetal. Now, I use LINQ to SQL, and I want to display the result of queries in a data grid view. when I do that, the columns are named after my columns in the DB table, and all the properties are displayed. I know I could change this by using DisplayName and Browseable attributes, but because the classes are auto-generated I can't just add this attributes where there are needed. I came up with three workarounds:
creating an Adopter to adopt my classes. I'm still not sure how exactly you make an adopter for this case.
creating another program that will run after the generating of the code that will add the attributes. this seems like an hack, and I prefer to separate between the functionality and the GUI, so this method is on hold.
using the MetaDataType attribute. I couldn't get this to work, and it's requires, as far as I know, that the classes and the metadata class will be in the same DLL.
How do I do the customization? is there another way? what way should I take and how?
EDIT: forgotten to mention: I'm using winforms, but if it will simplify things, I will move to WPF.
You can set a Types Metadata Type at run time by registering it with the TypeDescriptor manually.
That goes something like this.
var type = typeof(Foo);
var metadataType = typeof(FooMetadata);
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType), type);
To show it all in context this will show a single column in the data grid with the header "Custom Bar".
public class Foo
{
public string Bar { get; set; }
public string DontShowMe { get; set; }
}
public class FooMetadata
{
[DisplayName("Custom Bar")]
public string Bar { get; set; }
[Browsable(false)]
public string DontShowMe { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var type = typeof(Foo);
var metadataType = typeof(FooMetadata);
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType), type);
this.dataGridView1.DataSource = new List<Foo> { new Foo { Bar = "Foobar" } };
}
}
The is also a TypeDescriptor.RemoveProviderTransparent if you want to switch the Metadata Type on the go but keep in mind that setting/unsetting it applies to the whole application domain and so threading needs to be taken into account.
By using the WPF DataGrid you can easily customize the auto generated columns by using the AutoGeneratingColumn event.
Why can't you use the data grid view's Columns collection to change DisplayNames and Visibles at runtime?

Is there a good pattern for exposing a generic collection as readonly?

So I've got these classes that expose a collection of child objects.
I don't want other classes adding or removing objects from collections because I need to wire into events in the child objects, so as they get added or removed I want to be able to do additional processing. But I really love the ease of manipulating generics internally.
Did I mention this is a WPF app so I need INotifySupport?
The best I can come up with is something like this.
public class foo : INotifyPropertyChanged
{
protected List<ChildFoo> _Children = new List<ChildFoo>();
public foo()
{
}
public void AddChild(ChildFoo newChild)
{
DoAttachLogic(newChild);
_Children.Add(newChild);
NotifyPropertyChange("Children");
}
public void RemoveChild(ChildFoo oldChild)
{
DoRemoveLogic(oldChild);
_Children.Remove(oldChild);
NotifyPropertyChange("Children");
}
public ChildFoo[] Children
{
get
{
return _Children.ToArray();
}
}
}
Are there serious flaws with this design that I'm not seeing?
Every time the Children property is accessed we get the overhead of converting list to an array.
Any advice on this would be great.
This is what I do for normal code:
Public Readonly Property Childern As ObjectModel.ReadOnlyCollection(Of Child)
Get
Return New ObjectModel.ReadOnlyCollection(Of Child)(_ChildernList)
End Get
End Property
For WPF code I would just expose a subclass of ObservableCollection.
You should use ObservableCollection as field in your class, you then have full access to modify collection. Then expose this as ReadonlyObservableCollection via property.
And if you dont change collection itself (eg. nochildren = new ObservableCollection(), you should make field readonly), then you dont need any kind of notifyPropertyChanged on this property, because it doesnt change and collection itself handles those events for its children.
public class Child
{
public int Value { get; set; }
}
class MyClassWithReadonlyCollection
{
private readonly ObservableCollection<Child> _children = new ObservableCollection<Child>();
public MyClassWithReadonlyCollection()
{
_children.Add(new Child());
}
//No need to NotifyPropertyChange, because property doesnt change and collection handles this internaly
public ReadOnlyObservableCollection<Child> Children { get { return new ReadOnlyObservableCollection<Child>(_children); } }
}
I changed the "add child" and "remove child" to protected since you are saying you don't want other classes modifying your collection. I changed your List to ObservableCollection so you can recieve collection changed notifications. Since you are using an IList there is no need to call ToArray(), just access directly.
try this:
public class foo : INotifyPropertyChanged
{
protected ObservableCollection<ChildFoo> _Children = new ObservableCollection<ChildFoo>();
public foo() { }
protected void AddChild(ChildFoo oldChild)
{
DoAttachLogic(newChild);
_Children.Add(newChild);
NotifyPropertyChange("Children");
}
protected void RemoveChild(ChildFoo oldChild)
{
DoRemoveLogic(oldChild);
_Children.Remove(oldChild);
NotifyPropertyChange("Children");
}
public ChildFoo this[int n]
{
get
{
return _Children[n];
}
}
}
You could subclass BindingList and set AllowNew/AllowRemove to false. In your Child Add/Remove methods, you can set it to true, make the changes, then set it back to false. (Of course, you need to hide set access to AllowNew/AllowRemove from outside callers as well).
Another option - subclass Observable collection and override the InsertItem, RemoveItem, etc methods to behave as AddChild/RemoveChild would behave. Then callers can still access it in familiar ways, but not bypass your custom logic.
Subclassing an existing collection class is probably going to be easier (for you and the consumer) than wrapping a collection in another class.

Categories