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.
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
}));
}
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.
I have the main form class which contains a list box I want to change. The box is populated with items created in a time-consuming method. Right now it looks like this (inventing an example by hand, might not be valid C#):
List<string> strings = StaticClassHelper.GetStrings(inputString);
foreach(string s in strings)
{
listBox1.Add(s);
}
//meanwhile, in a different class, in a different file...
public static List<string> GetStrings(inputString)
{
List<string> result = new List<string>();
foreach(string s in inputString.Split('c'))
{
result.Add(s.Reverse());
Thread.Sleep(1000);
}
return result;
}
What I would like to do instead is regularly update the list box as new strings are found. The other answers I found work when the thread method is in the same class, so you can set up an event handler. What do I do here?
Here is how I like to do this, I create a method on the form like this:
public void AddItemToList(string Item)
{
if(InvokeRequired)
Invoke(new Action<string>(AddItemToList), Item);
else
listBox1.Add(Item);
}
I prefer invoke in this case to make sure the items are added synchronously, otherwise they can get out of order. If you don't care about the order then you can use BeginInvoke which will be a tad faster. Since this method is public, you can all it from any class in your application as long as you can get a reference to your form.
Another advantage of this is that you can call it from either your UI thread or a non-UI thread and it takes care of deciding whether or not it needs Invokeing. This way your callers don't need to be aware of which thread they are running on.
UPDATE
To address your comment about how to get a reference to the Form, typically in a Windows Forms app your Program.cs file looks something like this:
static class Program
{
static void Main()
{
MyForm form = new MyForm();
Application.Run(form);
}
}
This is typically what I would do, particularly in the case of a "Single Form" application:
static class Program
{
public static MyForm MainWindow;
static void Main()
{
mainWindow = new MyForm();
Application.Run(form);
}
}
And then you can access it pretty much anywhere with:
Program.MainWindow.AddToList(...);
The class containing the ListBox needs to expose a method to add a string - since this method might be called on a different thread, it needs to use
listBox1.Invoke( ...)
to create a thread-safe calling mechanism
Would it be possible for you to rewrite GetStrings as an Iterator? Then in your UI you could start a background thread which iterates over the results of GetStrings, updating the listbox each time. Something like:
public static System.Collections.IEnumerable GetStrings(inputString)
{
foreach(string s in inputString.Split('c'))
{
yield return s.Reverse();
Thread.Sleep(1000);
}
}
And in the UI (Assuming C# 4):
Task.Factory.StartNew(() =>
{
foreach (string s in StaticClassHelper.GetStrings(inputString))
{
string toAdd = s;
listBox1.Invoke(new Action(() => listBox1.Add(toAdd)));
}
}
Probably cleaner ways to go about it, but this should get you what you're looking for.
I'm designing a UI manager class that will manage all my UI elements (this is for an XNA game, so there is no existing UI framework) but I'm wondering how to deal with situations where I want the UI manager to have special access to data in the UI elements that other classes can't access.
For example, I want to have a SetFocus method to focus a specific element and this method needs to ensure that the previously focused element loses focus. The individual UI elements themselves can't handle this because they don't have access to the list of UI elements, which means the manager has to handle it, but how can I allow the manager and only the manager to set the variable on a UI element?
The thought occurs to me to just store the currently focused element on the manager, however I don't particularly like that solution and, given a single UI element, I would like to query it to find out if it has focus. Even if it makes sense to store the currently focused element on the manager since its just a single variable, there are other things I need to deal with that would require arrays to associate the data with the elements if its stored on the manager, and that just seems to defeat the purpose of OOP.
I know I don't need to have it so that the manager is the only one with access to this data, I could just make all the data public, but that's not good design...
What you are looking for is a C# equivalent of the C++ friend concept. As you read in the linked post 'the closest that's available (and it isn't very close) is InternalsVisibleTo' (citing Jon Skeet). But using InternalsVisibleTo in order to accomplish what you want would mean you'd have to break up your complete application into a library per class, which would probably create a DLL hell.
Building upon MattDavey's example got me:
interface IFocusChecker
{
bool HasFocus(Control control);
}
class Manager : IFocusChecker
{
private Control _focusedControl;
public void SetFocus(Control control)
{
_focusedControl = control;
}
public bool HasFocus(Control control)
{
return _focusedControl == control;
}
}
class Control
{
private IFocusChecker _checker;
public Control(IFocusChecker checker)
{
_checker = checker;
}
public bool HasFocus()
{
return _checker.HasFocus(this);
}
}
Whether a Control has focus is now only stored in the Manager and only the Manager can change the focused Control.
A little example how to put things together, for completeness:
class Program
{
static void Main(string[] args)
{
Manager manager = new Manager();
Control firstControl = new Control(manager);
Control secondControl = new Control(manager);
// No focus set yet.
Console.WriteLine(string.Format("firstControl has focus? {0}",
firstControl.HasFocus()));
Console.WriteLine(string.Format("secondControl has focus? {0}",
secondControl.HasFocus()));
// Focus set to firstControl.
manager.SetFocus(firstControl);
Console.WriteLine(string.Format("firstControl has focus? {0}",
firstControl.HasFocus()));
Console.WriteLine(string.Format("secondControl has focus? {0}",
secondControl.HasFocus()));
// Focus set to firstControl.
manager.SetFocus(secondControl);
Console.WriteLine(string.Format("firstControl has focus? {0}",
firstControl.HasFocus()));
Console.WriteLine(string.Format("secondControl has focus? {0}",
secondControl.HasFocus()));
}
}
Having a variable on a control itself which is implicitly relative to the state of other controls is probably a poor design decision. The concept of "focus" is of concern at a much higher level, either a window or better yet, a user (the user is the one doing the focusing). So it's at the window or user level that a reference to the focused control should be stored, but in addition to that, there ought to be a way for controls to be notified when they gain/lose focus from the user - this could be done via events on the user/manager or by any standard notification pattern.
class Control
{
public Boolean HasFocus { get; private set; }
internal void NotifyGainedFocus()
{
this.HasFocus = true;
this.DrawWithNiceBlueShininess = true;
}
internal void NotifyLostFocus()
{
this.HasFocus = false;
this.DrawWithNiceBlueShininess = false;
}
}
class User // or UIManager
{
public Control FocusedControl { get; private set; }
public void SetFocusOn(Control control)
{
if (control != this.FocusedControl)
{
if (this.FocusedControl != null)
this.FocusedControl.NotifyLostFocus();
this.FocusedControl = control;
this.FocusedControl.NotifyGainedFocus();
}
}
}
Disclaimer: I've never written a UI library and I may be talking complete nonsense..
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]; }
}
}