WPF Method binding to get() and set(value) instead of properties - c#

Is there any way to use binding which directly interacts with methods to get a value and set its value?
I have a class which has methods instead of properties
public class Foo
{
public object GetProp1()
{
//some logic
return someObject;
}
public object GetProp2()
{
//some logic
return someObject;
}
public object SetProp1(object someObject)
{
//some logic
}
public object SetProp2(object someObject)
{
//some logic
}
}
To achieve data binding, I'm declaring properties for each function and calling model's method
public class FooViewModel
{
Foo foo = new Foo();
public object Prop1
{
get => foo.GetProp1();
set => foo.SetProp1(value);
}
public object Prop2
{
get => foo.GetProp2();
set => foo.SetProp2(value);
}
}
Problem is if I have 50 methods, in Foo, I need to create approx 50 properties in ViewModel. Is there any other way to eliminate properties in ViewModel just to bind with View?
Something like this:
Textbox will set its value and Label will display
<TextBox Text="{MethodBinding MethodName=foo.SetProp1}"/>
<Label Content="{MethodBinding MethodName=foo.GetProp1}"/>

You can technically manage to bind to methods if you really put your mind to it but I don't think that's a very helpful answer.
In any commercial team I've worked in, doing this would see your MR/PR rejected. It is widely considered bad practice.
What I would recommend is make those properties regular properties raising property changed. Get your data for them in async tasks.
If you define an interface you can then give all your Getting-Data Tasks the same name and generically invoke them on any viewmodel you instantiate.
In some real world code I have, I call this Task Initiate.
interface IInitiatedViewModel
{
Task Initiate();
}
Here's one such task ( slightly simplified ) out one of my viewmodels:
public async Task Initiate()
{
DateFrom = LastApril();
Transactions = await repository.GetFlattenedTransactionsAsync(DateFrom, DateTo);
}
Transactions is a public property a Datagrid is bound to.
Process is therefore to instantiate my ViewTransactionsViewModel. It comes out a DI container but let's not digress too far.
I can present that viewmodel to the UI and it is templated out into a View.
It has no data initially.
Then await Initiate.
That sets my properties with data.
In more complicated scenarios, Initiate could start multiple threads with Tasks. Arranging data might be quite expensive so a Task (or many ) could be started on another thread.
A common variation is to have a base viewmodel which exposes an IsBusy bool. That is used to flag commands and drive a busy spinner. That would initially be true. Your view would render with a spinner. The last line of Initiate would be to set IsBusy false. The spinner would disappear and the date would be rendered.

Related

WPF ObservableCollection<T> of UI Framework Element Nested 2 Classes deep

I have created a custom FrameWorkElement ( Battery.cs ) to represent the data to the user in the UI. Within the Battery.cs class I had several dependencyProperties so the UI could monitor the various changes and re-render the object upon changes.
I placed that ObservableCollection within my MainWindowViewModel.cs, which was bound to the main view through a ListBox.
Everything was working properly however this was only for testing as I needed to move the collection down into another class which was going to manage / update the batteries. This management was going to happen asynchronously and thus I was running into a lot of problems with the DependencyProperties calls within the Battery.cs class as they were on the UI thread and not the management/process thread.
So I removed the DependencyProperties, and tried to move the DependencyProperty up to the MainWindowViewModel.cs. Now I am not getting errors about which thread has ownership and I can see that the Batteries in the ObservableCollection are being updated. However the OnRender method is never being called by the UI. So the Batteries are never being rendered/shown anymore.
Here is the code for the DependencyProperty in the MainWindowViewModel.cs
public static readonly DependencyProperty batteriesProperty = DependencyProperty.Register(
"Batteries",
typeof(ObservableCollection<Battery>),
typeof(MainWindow),
new UIPropertyMetadata(new ObservableCollection<Battery>()));
public ObservableCollection<Battery> Batteries
{
get { return tbModel.Modules[0].batteries; }
}
I think my main problem may be in this line
new UIPropertyMetadata(new ObservableCollection<Battery>()));
However I can't seem to figure out what it should be, or how to adjust the code such that the UI does update the graphics once I have called InvalidateVisual within the Battery.cs class.
public void UpdatePacket(Packet packet)
{
packet= packet;
Voltage = packet.Voltage;
InvalidateVisual();
}
The InvalidateVisual() method is executing however the OnRender override is never being executed.
Making ViewModel derived from DependecyObject is pointless. This only complicates and confuses the implementation.
The Batteries property of type ObservableCollection must be a regular CLR read-only property in the ViewModel.
public ObservableCollection<Battery>() Batteries {get;}
= new ObservableCollection<Battery>();
If an instance of the ViewModel exists in the entire session of the Application, then in the ViewModel constructor, synchronize the bindings to this collection.
protected static readonly Dispatcher Dispatcher = Application.Current.Dispatcher;
public MainWindowViewModel()
{
if (Dispatcher.CheckAccess())
{
BindingOperations.EnableCollectionSynchronization(Batteries, ((ICollection)Batteries).SyncRoot);
}
else
{
Dispatcher.Invoke(()=>BindingOperations.EnableCollectionSynchronization(Batteries, ((ICollection)Batteries).SyncRoot));
}
// Some Code
}
If the instances of the ViewModel can be destroyed, replace each other, then the synchronization of the bindings retains a reference to the instance of the ViewModel and therefore this instance will not be deleted by the GC. Also, the synchronization of bindings does not always provide thread safety for working with a collection.
In these cases, you are not using BindingOperations.EnableCollectionSynchronization ().
But instead, you always work with the collection only in the Dispatcher thread.
protected static readonly Dispatcher Dispatcher = Application.Current.Dispatcher;
public MainWindowViewModel()
{
// Some Code
}
... SomeMethod(...)
{
// Some Code
if (Dispatcher.CheckAccess())
{
Batteries.Add(...);
}
else
{
Dispatcher.Invoke(()=>Batteries.Add(...));
// Or
Dispatcher.InvokeAsync(()=>Batteries.Add(...));
}
// Some Code
}

Ensure GUI isn't locked up by incoming messages

I have a UserControl that was built following the MVVM pattern with an exposed function for other apps to send "commands" for the control to do. The commands in this case are strings. I'm trying to find a way to stop the GUI from hanging when a lot of commands are being sent in a short period. Each command should wait for the last one to finish.
Most of these commands do work on a 3rd party map control that is displayed in the main control's view.
The flow goes like this:
App sends command string to control.
Control calls a parse function to parse the string.
After parsing is complete, a certain class is called depending on the command.
Stuff happens i.e. create a model, update ObservableCollection, update the map control, etc.
Here's an example:
The usercontrol:
///The code behind for the control
public partial class MainControl : UserControl
{
public MainControl()
{
InitializeComponent();
}
//Other apps call this function
public void ExecuteCommand(string command)
{
CommandParser.StartParse(command);
}
}
Class to parse the commands:
//Handles parsing a string command and calling the right class
public static class CommandParser
{
public static void StartParse(string command)
{
//parses the command into a string array to hold different parts
DoCommand(parsedCommand);
}
private static void DoCommand(string[] command)
{
switch(command[0])
{
case "addpoint":
AddCommand.AddObj(command);
break;
case "createstyle":
CreateCommand.CreateObj(command);
break;
}
}
}
Two classes that take the parsed command and do something:
//Adds objects to the third party map control
public static class AddCommand
{
public static void AddObj(string[] command)
{
//Adds a point to the third party map control
MapControl.AddPoint(new Point(90, -90)); //just an example
}
}
//Creates model objects to add to observablecollections in viewmodels
public static class CreateCommand
{
public static void CreateObj(string[] command)
{
//create a model
//get the correct viewmodel
viewModel.StylesCollection.Add(styleModel); //StylesCollection is an ObservableCollection
}
}
Very basic example but should show the flow of everything. So imagine getting a a few thousands commands; Creating a model is fast, but because the map control (which is part of the GUI) is being updated every time, or an ObservableCollection (that has a control's itemsource bound to it) is being modified, the GUI hangs when receiving and doing all these commands.
In (the probably unlikely) case that there is a considerable amount of work that can be done off the UI thread, you may implement multi threading. A very basic way of doing this would be as so.
First, create a new thread to run:
var task = new Thread(YourTask);
task.Start();
Then in the thread method where the calculations are done, delegate the result to the UI thread by calling Dispatcher.Invoke. Make sure you don't call Invoke too often (e.g. not more than 10 times per second), as this will again block the UI thread.
public void YourTask()
{
// do calculations and get results
Application.Current.Dispatcher.Invoke(
new Action(() =>
{
// update the UI
}));
}

WPF await variable change then Update UI

Say I have a class that receives data over a TCP stream, parses it and changes it's properties accordingly.
public static class SomeClass
{
static bool myBool;
static string myMessage;
public static void ToggleBool()
{
myBool = !myBool;
// Do some other stuff here
}
public static UpdateMessage(string message)
{
System.Diagnostics.Debug.WriteLine(message);
ProcessMessage(message);
myMessage = message;
}
}
Now what I want to do is have a WPF "Debugging Window" that will visually display the settings. I want to basically run a loop that updates parts of the window accordingly.
Something like:
public partial class LogWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public Async Task UpdateUI()
{
while(checkForUpdates)
{
myCheckbox.IsChecked = await SomeClass.UpdatedBoolValue();
string newMessage = await SomeClass.NewMessageRCVD();
txtBox.Append(newMessage);
}
}
}
But that has 2 obvious issues. One, I have no idea how I would make a function that doesn't burn CPU by constantly checking with a while loop. I imagine I could use a getter/setter approach though. Two, I have to update both in order for that loop to run again.
What's the best approach to this? How do update just the parts of the UI that need to be updated?
EDIT: Similar question: Write an Async method that will await a bool
Depends on how complex a implementation/your needs are.
From your example if you made SomeClass implement INotifyPropertyChanged you could easily attach a WPF window to it, and through binding the window would update automatically without any form of a loop.
If your talking about multiple classes and you want to have them all display the property information in the same window, your best bet would probably be to create a queue. In each property you wish to keep track of have the setter write to the queue. (global or singleton) Then you can easily front that information in a window, or multiple via an Observer pattern. Can also set it up to it never writes to the queue in production, or with conditional compile statements production wouldn't even have the code if that is your desire.
The best way to do this is with data binding.
So we need to first define where our data is coming from. This is called the Context. This is going to come from a ViewModel which is an MVVM term. If you aren't aware of MVVM, don't worry, this can just come from any class you have. In the backend .xaml.cs code we need to add the class to our windows's DataContext. Here's what that looks like:
public partial class DebugView : Window
{
public DebugView()
{
InitializeComponent();
DataContext = new DebugViewModel();
}
}
And in our WPF's XAML file for the window we will have a label and textbox that is defined as such:
<Label Content="{Binding ClientCount, FallbackValue='Clients: 00'}" ... />
<TextBox Text="{Binding Port, UpdateSourceTrigger=PropertyChanged}" ... />
The text of a label is it's "content" while the text of a textbox is just "text." We add the binding keyword in there and now the text for each will be linked to the variables ClientCount and Port, repstively. So our DebugViewModel class will look like this at first:
private string _ClientCount;
public string ClientCount
{
get { return _ClientCount; }
set { _ClientCount= value; RaisePropertyChanged("ClientCount"); }
}
private string _Port;
public string Port
{
get { return _Port; }
set { _Port= value; RaisePropertyChanged("Port"); }
}
Now you don't have a Function called RaisePropertyChanged() so what I did (and I think is common practice) was I made a base class that implements the INotifyPropertyChanged and handles all the work there.
So our base class, called BaseViewModel will inherit from the INotifyPropertyChanged class, and setup everything for us. That just looks like this (feel free to just copy paste and use as is):
using System.ComponentModel;
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
internal void RaisePropertyChanged(string prop)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
// Other functions we want all ViewModels to have
}
and so then our DebugViewModel class will look like such:
public class ServerViewModel : BaseViewModel
{
private string _ClientCount;
public string ClientCount
{
get { return _ClientCount; }
set { _ClientCount= value; RaisePropertyChanged("ClientCount"); }
}
private string _Port;
public string Port
{
get { return _Port; }
set { _Port= value; RaisePropertyChanged("Port"); }
}
public DebugViewModel()
{
// Initialize to default values
ClientCount = $"Clients {server.clientCount}";
Port = $"{server.port}";
}
// Rest of code
}
And then when you start your program it will autopopulate the fields and you when you change the data in the textbox, the string will change, and vice versa. The UpdateSourceTrigger=PropertyChanged part of our XAML declaration makes it so that the variable is updated as soon as the data in the textbox is changed (default behavior is when the textbox loses focus. e.g. you tab to the next textbox or you click away).
This is pretty cool because you can validate input dynamically as it's typed, as well as not having to worry about switching to the UI thread to update the UI, and IMO makes the code look simpler just by having it bound like this.

How to bind the result of a method instead of a property of an object of a custom class in a Windows Universal App C#

I have a class with some properties and methods and I bind an ObservableCollection of objects of this class to a list view in a Windows Universal App.
However a lot of the things I want to display on each item of this list are properties of the class which are derived from other properties: for example I might have a Boolean about the object and then for the UI two colours representing true and false. This Boolean may may also the result of calculations between multiple float properties of the object
My question is can I bind the result of a method in the class to save me from calculating the other properties separately and having properties for those things?
I've looked into converters but they look like they operate on a single property and here I need to be able to act on multiple properties
Assuming you have this
public class MyClass
{
public bool MyBool {get; set;}
public Color MyColor()
{
if (this.MyBool) return Colors.Green;
else return Colors.Red;
}
}
And you want to bind MyColor, you could just make it a readonly property.
public class MyClass
{
public bool MyBool {get; set;}
public Color MyBoolColor { get { return this.MyBool ? Colors.Green : Colors.Red; }
}
You then proceed to bind and use MyBoolColor as you wish. Basically, what you now have as methods beceome the get part of read-only properties.
This is of course a very short proof-of-concept which might need to be adapted to your actual code.

Update binding without DependencyProperty

I have a lot of existing business objects with many properties and collections inside which I want to bind the userinterface to. Using DependencyProperty or ObservableCollections inside these objects is not an option. As I know exactly when I modify these objects, I would like to have a mechanism to update all UI controls when I do this. As an extra I also don't know which UI controls bind to these objects and to what properties.
Here is a simplified code of what I tried to do by now:
public class Artikel
{
public int MyProperty {get;set;}
}
public partial class MainWindow : Window
{
public Artikel artikel
{
get { return (Artikel)GetValue(artikelProperty); }
set { SetValue(artikelProperty, value); }
}
public static readonly DependencyProperty artikelProperty =
DependencyProperty.Register("artikel", typeof(Artikel), typeof(MainWindow), new UIPropertyMetadata(new Artikel()));
public MainWindow()
{
InitializeComponent();
test.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
artikel.MyProperty += 1;
// What can I do at this point to update all bindings?
// What I know at this point is that control test or some of it's
// child controls bind to some property of artikel.
}
}
<Grid Name="test">
<TextBlock Text="{Binding Path=artikel.MyProperty}" />
</Grid>
This is, I tried to pack my object into a DependencyProperty and tried to call UpdateTarget on this, but didn't succeed.
What could I do to update the corresponding UI controls?
I hope I described my situation good enough.
Using INotifyPropertyChanged is a good alternative to DependencyProperties.
If you implement the interface you can raise the PropertyChanged event with null as parameter to notify the UI that all properties changed.
(I'm going to assume you can't add INotifyPropertyChanged to your business objects either, and that you don't want to add another "view of the data model" layer of wrapper objects a la MVVM.)
You can manually update bound properties from their data source by calling BindingExpression.UpdateTarget().
myTextBlock.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();
To update all bindings on a control or window, you could use something like this:
using System.Windows.Media;
...
static void UpdateBindings(this DependencyObject obj)
{
for (var i=0; i<VisualTreeHelper.GetChildrenCount(obj); ++i)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child is TextBox)
{
var expression = (child as TextBox).GetBindingExpression(TextBox.TextProperty);
if (expression != null)
{
expression.UpdateTarget();
}
}
else if (...) { ... }
UpdateBindings(child);
}
}
If you're binding a diverse set of properties then rather than handling them individually as above, you could combine the above with this approach to enumerate all dependency properties on a control and then get any BindingExpression from each; but that relies on reflection which will not be particularly performant.
As a footnote, you can also use BindingExpression.UpdateSource() if you want to explicitly write back to the data source. Controls usually do this anyway when their value changes or when they lose focus, but you control this and do it by hand with {Binding Foo, UpdateSourceTrigger=Explicit}.
As I know exactly when I modify these objects, I would like to have a mechanism to update all UI controls when I do this.
You will find that the most straightforward and maintainable way to deal with this is to implement view model classes for each class you want to present in the UI. This is probably true if you can modify the underlying classes, and almost certainly true if you can't.
You don't need to be using dependency properties for this. Dependency properties are only necessary on the targets of binding, which is to say the controls in the UI. Your view model objects are the source; they need only implement INotifyPropertyChanged.
Yes, this means that you will need to build classes that contain a property for each property exposed in the UI, and that those classes will need to contain observable collections of child view models, and you'll have to instantiate and populate those classes and their collections at runtime.
This is generally not as big a deal as it sounds, and it may be even less of one in your case. The traditional way to build a view model that's bound to a data model is to build properties like this:
public string Foo
{
get { return _Model.Foo; }
set
{
if (value != _Model.Foo)
{
_Model.Foo = value;
OnPropertyChanged("Foo");
}
}
}
But if, as you've claimed, you know when the objects are being updated, and you just want to push the updates out to the UI, you can implement read-only properties, and when the underlying data model gets updated make the view model raise PropertyChanged with the PropertyName property of the event args set to null, which tells binding, "Every property on this object has changed; update all binding targets."

Categories

Resources