Fairly new to C# and WPF, so apologies if this is obvious.
I have converted a console app into a WPF application, and what I wanted to do was pass all the Console.WriteLine lines to an observable collection, to then write the newly added lines to a text box, to show the progress in the UI.
The issue I have is that there are multiple classes that I want to write to this ObservableCollection from, and this is the point I am stuck at.
Here is how I have approached this so far...
Model - I have the observable collection defined:
public class Progress
{
private ObservableCollection<string> _GGProgress = new ObservableCollection<string>();
public ObservableCollection<string> GGProgress
{
get { return _GGProgress; }
set { _GGProgress = value; }
}
}
MainWindow.xaml.cs - Create instance of the progress class and attach a CollectionChanged event to it:
//Initiage the Progress class
Progress prog = new Progress();
public MainWindow()
{
InitializeComponent();
prog.GGProgress.CollectionChanged += GGProgress_CollectionChanged;
}
private void GGProgress_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null && e.NewItems.Count > 0)
{
GGImportProgress.Text += e.NewItems[0];
}
}
I can then call this within the MainWindow.xaml.cs:
prog.GGProgress.Add("Text added");
And it will update the textbox as expected. From the MainWindow.xaml.cs however, I will initiate further classes, and within those classes, I could initiate further classes, and it is from all these classes I want to be able to update the observable collection and have it update that text box in the UI.
I tried initiating the Progress class, and then accessing it through the class I initiated it from, but I soon hit circular references.
I can look at rewriting the whole thing, but I wondered if I was missing something and there is an elegant solution for me to implement to be able to update that observable collection from different nested classes?
I am not using MVVM at present, as I presumed that this would be an small 'easy' app, but will happily switch if it makes life easier.
Related
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
}));
}
I heave two user controls.
The first one contains one ItemsControl and I have to fill it dynamically with my second user control. The second user control may contains another ÌtemsControl itself recursively so it is quite a heavy control to initialize.
Here is what I did for the SubControl:
public sealed partial class SubControl : UserControl
{
public SubControl(ModelA item)
{
this.InitializeComponent();
this.DataContext = item;
//if the current item has child items, add them to the ItemsControl:
if(item.SubItems != null)
{
BuildUI(item.SubItems);
}
}
private void BuildUI(List<AbstractModel> data)
{
foreach(var item in data)
{
var dataItem = item as ModelA;
//we only want item of type ModelA in our ItemsControl:
if(dataItem != null)
{
SubElements.Items.Add(new SubControl(dataItem));
}
}
}
}
Now here is what I wrote first for the MainControl whose ItemsControl (called Elements) will contain a set of these SubControl:
public sealed partial class MainControl : UserControl
{
public MainControl(List<AbstractModel> data)
{
this.InitializeComponent();
BuildUI(data);
}
private void BuildUI(List<AbstractModel> data)
{
foreach(var item in data)
{
var dataItem = item as ModelA;
if(dataItem != null)
{
Elements.Items.Add(item);
}
}
}
}
I noticed small freezes of the UI while this "tree" was being built, but I currently work on a very powerful computer. I would like the application to run smoothly even on lesser devices such as the Windows Surface RT. So I changed the MainControl code:
public sealed partial class MainControl : UserControl
{
public MainControl(List<AbstractModel> data)
{
this.InitializeComponent();
BuildUI(data);
}
private async void BuildUI(List<AbstractModel> data)
{
var list = new List<SubControl>();
await Task.Run(() =>
{
foreach(var item in data)
{
var dataItem = item as ModelA;
if(dataItem != null)
{
list.Add(new SubControl(dataItem));
}
}
});
foreach(var item in list)
{
Elements.Items.Add(item);
}
}
}
The idea is to build all the SubControl in a different thread so the UI is not blocked, and when all the user controls have been initialized we would add them to the ItemsControl in the MainControl.
However this does not work because of the marshalling of the data, even though not a single SubControl is actually present on the UI! It crashes while building the SubControl which is really weird, because it does not have any impact on the actual UI; they are just added to a temporary List.
What could be a trick to build these user controls in a background task so the UI does not freeze?
Windows UI is very single-threaded. Each UI control must be created and only used from a single thread. There is no way around this.
So, it's time to think about the solution a bit differently. It's no problem to create dozens of controls; the UI would handle that just fine. You're talking about adding hundreds or thousands of items to a list control, and that's just an unusable UI. So the proper solution is to rethink your UI design. Perhaps you could divide the results into categories or something.
If you've thought about your UI design and are still sure that you want to display hundreds or thousands of items to the user, then the answer is to use virtualization. This is a bit harder to code than just a simple foreach loop, but it is the only way to efficiently display large amounts of data.
There are many ways to do this.
You could use this workflow:
Implement a EventHandler, that is raised on initialiazation finish.
Show an empty MainControl with saying "loading"
Build all UIs in another Thread
When built all UIs, raise the finish event
A listener to this event will add the SubControls to your MainControl
[...] because it does not have any impact on the actual UI; they are just added to a temporary List
By the way: Your code does crash, because you are adding the Controls in a async method, which is actually a non GUI-thread. So you are wrong with your proposition.
I have a WPF/C# program with several classes, and the MainWindow.cs class has user controls which I'd like to update with the status of the computation occurring inside other classes. After googling around and borrowing from examples, I figured out how to set up an Event inside the other class, and invoking it when something changed. Then as long as the main class has a handler tied that event, I could appropriately update UI stuff (status bars, etc). Below is a stripped-down version of what I'm doing:
namespace Program
{
public partial class MainWindow : Window
{
public void SetUpHandler()
{
TestA.WorkerProgressThingie += new ProgressChangedEventHandler(TestA_ProgressChanged);
}
void TestA_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage
}
}
public class TestA
{
public static event ProgressChangedEventHandler WorkerProgressThingie;
public static void SomeFunction()
{
int value = 0;
//...(some boring code that does something here)
ProgressChangedEventArgs e = new ProgressChangedEventArgs(value, null);
if (WorkerProgressThingie != null)
WorkerProgressThingie.Invoke(null, e)
}
}
}
Is there not a way to simply call the progressBar property from the other class? (i.e. MainWindow.progressBar.Value)?
What is the purpose of the "object sender" parameter when I invoke the event, and how is it supposed to be used normally? The examples I see always use 'null'.
Thanks!
1) Yes, you can access any part of any class if it is declared public. In this case, you could declare the progressBar control as public, and anything that has a reference to class MainWindow can fiddle with it. HOWEVER, this would be pretty poor practice. Instead, you could bind to some 'value' which updates in relation to the current progress of the activity and let the MainWindow class worry about how it represents that change (in this case by updating a ProgressBar),
2) object sender in all events is meant to be a reference to the object which raised the event, so the event consumer knows where the event came from. Using null is also poor practice IMO, and in general, an object which raises an event should do so like;
SomeEvent(this, someEventArgs);
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."
I have a Panel I want to fill with some UserControl(s) at runtime. These controls are complex and may be interdependent, so I'd like them:
to be editable with Visual Studio designer;
to be in the same context (= defined in the same class);
Both of the requirements are a must-have.
Considering UserControl is itself an indexed collection of Control(s), I started designing my controls in the same class, without caring about real positioning of them (that will be specified runtime). I already used the same identical approach with DevComponents ribbon containers (with much satisfaction), so I initially thought the same was possible with standard UserControl(s).
I eventually realized that a Control can be inside only one Control.ControlCollection instance at a time, so I couldn't use the Controls property and add controls to another panel without removing them from the original "dummy" UserControl.
My question is: is there a clean and supported way to create this designer-aware UserControl collection? I would call this approach a pattern because it really adds code clearness and efficiency.
Thanks,
Francesco
P.S.: as a workaround, I created a DummyControlContainer class that inherits UserControl and keeps a Dictionary map filled at ControlAdded event (code follows). Wondering if there's something cleaner.
public partial class DummyControlContainer : UserControl
{
private Dictionary<string, Control> _ControlMap;
public DummyControlContainer()
{
InitializeComponent();
_ControlMap = new Dictionary<string, Control>();
this.ControlAdded += new ControlEventHandler(DummyControlCollection_ControlAdded);
}
void DummyControlCollection_ControlAdded(object sender, ControlEventArgs args)
{
_ControlMap.Add(args.Control.Name, args.Control);
}
public Control this[string name]
{
get { return _ControlMap[name]; }
}
}
After testing and using it in a real world project, I'm more and more convinced that my solution was clean and safe if you need such a pattern. This container is intended to be filled with controls such as panels or similar. To prevent some bad behaviors with bindable data sources, I provided each new control added to this container with its own BindingContext. Enjoy.
public partial class DummyControlContainer : UserControl
{
private Dictionary<string, Control> _ControlMap;
public DummyControlContainer()
{
InitializeComponent();
_ControlMap = new Dictionary<string, Control>();
this.ControlAdded +=
new ControlEventHandler(DummyControlCollection_ControlAdded);
}
void DummyControlCollection_ControlAdded(object sender,
ControlEventArgs args)
{
// If the added Control doesn't provide its own BindingContext,
// set a new one
if (args.Control.BindingContext == this.BindingContext)
args.Control.BindingContext = new BindingContext();
_ControlMap.Add(args.Control.Name, args.Control);
}
public Control this[string name]
{
get { return _ControlMap[name]; }
}
}