HowTo write a recursive Custom Control in WinForms .NET - winforms

I am attempting to write a 'User Control' in WinForms .NET (not ASP.NET). The control is relatively simple. It will contain a label, a button, and a DataGridView.
However, the control needs to be able to instantiate itself, i.e. when the user clicks the button (of the parent control) at least 1 nested (children) control of the same type will be displayed underneath (kind of like a Tree)
I am having no success writing such a recursive control using a 'User Control'. A StackOverflow Exception occurs when instantiating MyControl within it's own constructor.
Therefore, I am leaning towards using a 'Custom Control', hoping it can handle the instantiation of itself (maybe in the Paint event??). Alot more work has to go into a Custom Control however, so I don't want to go down this path if it's going to take forever. I am on a tight deadline.
Anybody done this using a Custom Control or have any solid ideas on how to create a recursive control?
By the way, this control would be used in a fairly finite number of recursive combinations, so maybe it would be better to create a separate control for each parent/children scenario? I am thinking that would result in at least 10 separate user controls.
thanks for your help
UPDATE (here is my initial attempt at a stop condition per your feedback, but this is still causing children to be created indefinitely) :
public partial class CustomX : UserControl
{
private IList _children = new List();
public CustomX()
{
InitializeComponent();
Recurse(0);
}
private void Recurse(int childCount)
{
if (childCount

The problem is probably that the child control also instantiates a child control. There has to be a stop condition or controls will be generated until the stack overflows.
This should work:
public partial class CustomX : UserControl
{
private IList _children = new List();
public CustomX(int depth)
{
InitializeComponent();
if(depth > 0)
{
CustomX child = new CustomX(depth-1);
this.Controls.Add(child)
}
}
}

You should have no problem doing this with a user control. It is more likely an issue with not terminating the recursion properly. It (might) be more readable to perform the control creation in just the topmost parent control rather than delegating that task into each constructor.
Can you post the code you have in your constructor?

Related

Windows Narrator reads the names of all the controls in the window (even hidden ones)

I need to make my application visually impaired friendly... and I am facing this problem: Windows Narrator reads all the controls names in the window despite that some of them are hidden.
I have another app that I used WinForms to write it, and there it works fine.
After looking in the UI Spy I saw that WinForms app is not exposing hidden controls and WPF is exposing all the controls in the window.
Can it be that it's a bug in WPF?
I was having the same problem.
Based on Alexis's answer, I wrote the code bellow. It works for me.
public class MyAutoComplete : RadAutoCompleteBox
{
public MyAutoComplete ()
{
//init stuff here
}
protected override AutomationPeer OnCreateAutomationPeer()
{
return new MyAutomationPeer(this);
}
}
internal class MyAutomationPeer : RadAutoCompleteBoxAutomationPeer
{
public MyAutomationPeer(FrameworkElement owner)
: base(owner)
{
}
protected override List<AutomationPeer> GetChildrenCore()
{
return new List<AutomationPeer>();
}
}
If your controls are already in the visual tree, this behavior is the normal one, because UI Automation tree based on the Visual tree. So if you want to prevent of reading unnecessary elements using screen readers, you have to load them on demand.
You can also override the OnCreateAutomationPeer method in controls that contain visible and hidden elements to return your own AutomationPeer. Then you can override the GetChildrenCore method and return modified children collection. To update automation children tree, you need to call the AutomationPeer.ResetChildrenCache() method and the AutomationPeer.RaiseAutomationEvent(AutomationEvents.StructureChanged) one.

Prism, Unity, and Multiple Views ala MDI

I'm trying to create an application similar to Visual Studio in that we have a main content area (i.e. where documents are displayed in a TabControl, not a true MDI interface), with a menu on the side.
So far, I have everything working, except the content. My goal is that when a user double clicks on an item in the navigation menu on the side, it opens the document in the Content region. This works, but every time I double click it spawns a new instance of that same view. There's a chance that I could have multiple views of the same type (but different "names") in the TabControl content container.
Right now, my code looks something like this...
IRegion contentRegion = IRegionManager.Regions[RegionNames.ContentRegion];
object view = IUnityContainer.Resolve(viewModel.ViewType, viewModel.UniqueName);
if (!IUnityContainer.IsRegistered(viewModel.ViewType, viewModel.UniqueName))
{
IUnityContainer.RegisterInstance(viewModel.UniqueName, view);
contentRegion.Add(view);
}
contentRegion.Activate(view);
However, it appears that the view is never registered, even though I register it... I imagine I'm probably doing this wrong -- is there another way to do this? (re: the right way)
So, the problem was trying to do it this entire way. The smart method (for anyone else trying to do this) is to make use of Prism the correct way.
What I ended up doing was instead Navigating by:
1. In the Navigation Menu, constructing a UriQuery (included in Prism) with the UniqueID of the view I want to display (which is guaranteed to be unique) and adding that to the View I wanted to navigate to, i.e.:
IRegionManager.RequestNavigate(RegionNames.ContentRegion, new Uri(ViewNames.MyViewName + query.ToString(), UriKind.Relative));
where query is the UriQuery object.
2. Register the View and ViewName in the Module via:
IUnityContainer container = ServiceLocator.Current.GetInstance<IUnityContainer>();
container.RegisterType<object, MyView>(Infrastructure.ViewNames.MyViewName);
3. In the View, make sure the ViewModel is a parameter on the constructor. Let Prism inject this manually for us. Inside the constructor, make sure you set the DataContext to the incoming ViewModel.
4. Finally, make sure your ViewModel implements INavigationAware interface... This is a very simple implementation of it (UniqueID is a property on the ViewModel):
public virtual bool IsNavigationTarget(NavigationContext navigationContext)
{
if (navigationContext.Parameters != null)
return (navigationContext.Parameters["UniqueID"] == UniqueID);
return false;
}
public virtual void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public virtual void OnNavigatedTo(NavigationContext navigationContext)
{
if (navigationContext.Parameters != null)
UniqueID = navigationContext.Parameters["UniqueID"];
}
From here, Prism will ensure that only one view of your "UniqueID" will exists, while allowing for others of the same view, but different ViewModel (or data for that ViewModel, i.e. viewing two users in different tabs, but both use the same templated view).

wpf BackgroundWorker - Regarding updating UI

I use a browse for files dialog to allow a user to select multiple images. If a lot of images are selected, as expected it takes a bit. Below is an example of what I do with the selected images. I loop through the filepaths to images and create an instance of a user control, the user control has an Image control and a few other controls. I create the instance of this control then add it to a existing stackPanel created in the associating window xaml file. The example just below works fine, but I'm trying to understand BackGroundWorker better, I get the basics of how to set it up, with it's events, and pass back a value that could update a progress bar, but because my loop that takes up time below adds the usercontrol instance to an existing stackPanel, It won't work, being in a different thread. Is BackGroundWorker something that would work for an example like this? If so, what's the best way to update the ui (my stackpanel) that is outside the thread. I'm fairly new to wpf and have never used the BackGroundWorker besides testing having it just update progress with a int value, so I hope this question makes sense, if I'm way off target just let me know. Thanks for any thoughts.
Example of how I'm doing it now, which does work fine.
protected void myMethod(string[] fileNames) {
MyUserControl uc;
foreach (String imagePath in fileNames) {
uc = new MyUserControl();
uc.setImage(imagePath);
stackPanel.Children.Add(uc);
progressBar.Value = ++counter;
progressBar.Refresh();
}
}
below this class i have this so I can have the progressBar refresh:
public static class extensionRefresh {
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement) {
uiElement.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);
}
}
Check out this article on
Building more responsive apps with the Dispatcher
Now that you have a sense of how the Dispatcher works, you might be surprised to know that you will not find use for it in most cases. In Windows Forms 2.0, Microsoft introduced a class for non-UI thread handling to simplify the development model for user interface developers. This class is called the BackgroundWorker
In WPF, this model is extended with a DispatcherSynchronizationContext class. By using BackgroundWorker, the Dispatcher is being employed automatically to invoke cross-thread method calls. The good news is that since you are probably already familiar with this common pattern, you can continue using BackgroundWorker in your new WPF projects
Basically the approach is
BackgroundWorker _backgroundWorker = new BackgroundWorker();
// Set up the Background Worker Events
_backgroundWorker.DoWork += _backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted;
// Run the Background Worker
_backgroundWorker.RunWorkerAsync(5000);
// Worker Method
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Do something
}
// Completed Method
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Doing UI stuff
if (e.Cancelled)
{
statusText.Text = "Cancelled";
}
else if (e.Error != null)
{
statusText.Text = "Exception Thrown";
}
else
{
statusText.Text = "Completed";
}
}
Using a BackgroundWorker alone won't solve your issue since elements created during the DoWork portion will still have originated from a non-UI thread. You must call Freeze on any objects you intend to use on another thread. However only certain UI objects will be freezable. You may have to load in the images as BitmapImages on the background thread, then create the rest of your user control on the UI thread. This may still accomplish your goals, since loading in the image is probably the most heavyweight operation.
Just remember to set BitmapImage.CacheOption to OnLoad, so it actually loads up the image when you create the object rather than waiting until it needs to be displayed.

How do you call identically named properties on different types which don't share an interface?

I have a DataTemplate that needs to set the IsSelected property on an ItemsControl's container (such as TreeViewItem, ListViewItem or ComboBoxItem). However, it doesn't know the type of the container until it's passed in to it. Since IsSelected isn't part of a common base class or interface, nor is it a common dependency property registered with AddOwner to the various classes (Duh, MS!!! WTF not?!!), I ended up with this mess...
if (container is TreeViewItem) {
(container as TreeViewItem).IsSelected = true;
return;
}
if (container is ListBoxItem) {
(container as ListBoxItem).IsSelected = true;
return;
}
if (container is ComboBoxItem) {
(container as ComboBoxItem).IsSelected = true;
return;
}
...which not only is verbose, but requires me to modify it if I ever use a different ItemsControl that uses different container types! Not good!
Sure I could enhance it a little by putting this logic in extension methods (damn C# for not having extension properties!!) called IsContainerSelected and SetContainerSelected and putting them on UIElement, then moving the above code inside there, but it's just making the outside neater. The inside is still a mess.
My only other thought is to use reflection and look for an IsSelected property and use that if found, but I'm always leery of doing things like that. However, since there isn't a common interface or base class, I'm not really sure I have a choice here.
For context, I'm sharing a complex data template between several different ItemsControls and the template itself has controls that can receive focus such as checkbox and textbox. However, when those controls receive focus via the mouse, the underlying container item doesn't get selected and whatever was selected before remains so.
My workaround is to use an attached behavior that utilizes the preview events to intercept the focus before it happens and set the underlying item accordingly, which works great when I've hard-coded TreeViewItem or ListBoxItem, etc., but I don't want to hard-code the type since the control shouldn't really care. So that's the part that breaks down.
Ugh!!! Why didn't MS just register the same attached property or at least create an ISelectableContainer interface?!!
I have read your answer, and it does make sense - in your case, IsSelected may obviously be part of the ViewModel, and that seems to be the best solution in your case.
But you asked for further explanation about C# dynamic features. C# 4.0 now has some dynamic functionalities, which allow us to create code that would only be possible in languages like Python, Ruby or JavaScript. This, of course, has its cost - a dynamic abuse would not only make code slower, but also more confusing - because you would lose compile-time errors and IntelliSense.
I have written a simple example so you may understand it better:
public class ClassOne
{
public int SameProperty { get; set; }
}
public class ClassTwo
{
public int SameProperty { get; set; }
}
public class ClassThree
{
public string SameProperty { get; set; }
}
public partial class Form1 : Form
{
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
dynamic wrapper = new ClassOne();
wrapper.SameProperty = 5;
wrapper = new ClassTwo();
wrapper.SameProperty = 15;
wrapper = new ClassThree();
wrapper.SameProperty = "Now it is a string!";
// And now a run-time error...
wrapper.AnotherProperty = "And this won't work...";
}
}
As you can see, wrapper has no definite type whatsoever - a dynamic reference will allow any kind of method or property invocation, since the actual binding will only be made during run-time, not compile-time.
Of course, this example is very naive, but sometimes dynamic code may be useful - it is a good option to avoid explicit reflection, or to avoid long if...else statements based on type (like your snippet above).
I'm not sure that I fully understand your problem, but you could try adding an IsSelected boolean to your model and then binding that property against the Item control it's contained in. That way, you just have to worry about setting that property in the model, regardless of the container.
Per #mdm20's answer, he suggested modifying the ViewModel, which is of course normally what you want to do. However this is a purely view-related issue (keyboard navigation-related) and isn't reflected in the ViewModel at all, nor in this case should it be.
But that gave me an idea! Since I'm using a custom control to render the item in whichever items control (via its data template) it's being added to, that control naturally does have multiple instances (all of which are pointing to the same ViewModel instance), which is what I want!
Therefore, rather than adding the IsSelected to the ViewModel, I added it to the user control itself, then I just bind to that within the data template for the respective ItemsControl which I do know about. I can then set the IsSelected property in the code-behind for the user control as needed (i.e. during the preview mouse events, etc.) and the underlying ItemsControl responds appropriately! Works great and keeps the ViewModel clean since neither the model, nor the viewmodel need to know about it. The IsSelected remains purely in the UI which is where in this particular case it should be!

WPF event that fires after element is fully parented, but before it's arranged

I'm writing a WPF control that dynamically changes its contents depending on what types of Window/UserControl descendants are in its parentage list (part of an experiment with convention vs. configuration).
As such, I need some code to run after my control is fully parented (i.e. all the way up to the Window that's being shown). Ideally, I'd also like my code to run before the first Measure/Arrange pass, since my code is going to change the control's contents and force another Measure/Arrange pass.
I've looked at EndInit, but it fires after the control is loaded from XAML, at which time it might not be fully parented. (For example, if my control was on a UserControl, then EndInit will fire once the UserControl is loaded -- but before it's parented to anything else. I want to wait until the UserControl is parented to something, and that's parented to something else, all the way up.)
Currently I'm just hooking the Loaded event from my control's constructor, and running my code there (oddly enough, WPF doesn't have an OnLoaded method to override):
public class MyControl
{
public MyControl()
{
Loaded += (sender, e) => { ... };
}
}
This works -- it fires when the parents are fully populated -- but it's slightly less than optimal, because there's a Measure/Arrange pass that happens before Loaded.
Is there a good place I can put my code so that it runs after the Parents are set all the way up, but before the first Measure/Arrange pass?
Extra coolness points for solutions that would also work in Silverlight, ElementHost, the Blend/VS designer, and VisualBrush (i.e., not assuming that the top-level parent is a Window, or in the case of VisualBrush, not assuming that there even is a parent -- just that it's as parented as it's gonna be before showing up on the screen, or being sent to the printer, or whatever).
I believe the parents are all set in a single dispatcher operation, so you should be able to get that behavior by putting your logic in a delegate and queuing it up as the next dispatcher operation after the parent is set:
protected override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
this.Dispatcher.BeginInvoke(new Action(OnReady));
}
private void OnReady()
{
// Element should be fully parented here
}
You could also do that from EndInit rather than OnVisualParentChanged if you want to handle the case of no parent, although EndInit appears to be called more than once so you will need to check for duplicates:
private bool readyQueued;
public override void EndInit()
{
base.EndInit();
if (!readyQueued)
{
this.Dispatcher.BeginInvoke(new Action(OnReady));
readyQueued = true;
}
}
private void OnReady()
{
readyQueued = false;
// Element should be fully parented here
}

Resources