WPF tutorial for creating a custom usercontrol - wpf

I'm looking for a tutorial that explains creating custom usercontrols in WPF.
I want to have one control that combines a textblock, a textbox, and a button that launches a common fileopen dialog. I have the layout done and everything wired up. It's works but it's three independent controls. Hopefully there is a tutorial out there that explains how to turn this into one usercontrol and wire up everything so I can expose certain properties of the control, like the text in the textbox, to the rest of my WPF app.

This looks like a good article that might help.
http://www.c-sharpcorner.com/uploadfile/mahesh/user-control-in-wpf/
In fact, it looks like he's doing exactly what you're trying to do. As for accessing the TextBox contents from outside the user control, create a public property as shown in the article.
public string FileName
{
get { return FBCTextBox.Text; }
set { FBCTextBox.Text = value; }
}

Related

Hosted Winform control does not respond to events from WPF

This is my first question in StackOverflow. Due to lack of reputations, I couldn't post any links or images.
I've been working on the following issue for more than 2 days. Any help would greatly be appreciated.
Before I get into my question, here is what I have and what I'm expecting:
I have a Windows Form which hosts WPF in an ElementHost control.
And then, I have a Winforms UserControl similar to DateTimePicker. This
is hosted inside a WindowsFormsHost control.
The above scenario is un-avoidable for the following reasons:
The authorization dialog to all our applications is developed in
Winforms, and takes a Winforms instance as its parameter. There is no
WPF version introduced yet. Therefore, I had to use an ElementHost to
host my View inside the Windows Form.
The Winforms control hosted inside my WPF is also un-avoidable. We
have our own DateTime Winforms UserControl that behaves similar to
the DateTimePicker Winforms control, but has lot more complexities
involved. So, replacing this control with a WPF version is out of
question.
Expected Functionality:
I have a
WPF control (say, a textbox)
A DateTime Winforms UserControl that I was mentioning above.
And a Cancel button that basically resets the above controls.
When I hit the Cancel button, I'm publishing an event from the ViewModel, say RunViewModel to the WPF UserControl code behind file, say RunView.xaml.cs.
eventAggregator.GetEvent<ResetDateTimeEvent>().Publish(true);
In the code behind file, I've subscribed to the event as follows
eventAggregator.GetEvent<ResetDateTimeEvent>().Subscribe(ResetDateTimeHandler);
The WPF control resets to its default value, but the DateTime UserControl does not reset.
So, for testing purposes, I removed the ElementHost control, and just had my WPF View with a WindowsFormsHost control that hosts the DateTime Winforms UserControl, and a WPF "Cancel" button.
When I click on the button, the value on the DateTime control resets to its default value.
Then, I thought this might be an issue with my DateTime Winforms UserControl.
So, I replaced my DateTime Winforms UserControl with a Winforms Textbox control in my actual application. So now the nesting is as follows:
WinForms-ElementHost-WPF-WindowsFormsHost-Winforms Textbox
Here is the xaml code.
<WindowsFormsHost x:Name="ReportFromDtTmHost" Margin="8,0" Grid.Column="0"
LostFocus="ReportFromDtTmHost_LostFocus">
<WindowsFormsHost.Child>
<winforms:TextBox x:Name="ReportFromDateTime"/>
</WindowsFormsHost.Child>
</WindowsFormsHost>
On Initial load, I’m loading the Textbox with Initial Load Text text
private void Window_Loaded(object sender, EventArgs e)
{
ReportFromDateTime.Text = "Initial Load Text";
}
As I was mentioning above, when I hit the Cancel button, this is what happens:
Publish the event from ViewModel
eventAggregator.GetEvent().Publish(true);
Subscribe to the event in the code behind file (xaml.cs):
eventAggregator.GetEvent().Subscribe(ResetDateTimeHandler);
EventHandler for the published event.
private void ResetDateTimeHandler(bool cancelClicked)
{
ReportFromDateTime.Text = "Reset to Default";
}
As you can see in the above code, I’m resetting the Text on clicking the Cancel button.
During Debugging, I could see the Text property being changed to "Reset to Default", but the UI does not show these changes.
Here is the wierd part:
The Child property on the WindowsFormsHost control is different from the actual “ReportFromDateTime” Textbox control.
While debugging, I could see that the Child and Name property on the WindowsFormsHost control were different.
The Name property is empty,
ReportFromDtTmHost.Child.Name = ""
which rather should be ReportFromDateTime.
It almost seems like the Host and the Child controls are getting re-created.
As far as I see it, I think the extra level of nesting (WinForms-ElementHost-WPF-WindowsFormsHost-Winforms Textbox) might be causing issues during the interoperations between WPF and Winforms.
I’ve done a lot of research and searched lot of links for suggestions. I found none pointing out this issue. Some of them were close. Here are a couple of links:
The this suggests to reproduce the message loop under the “Surrogate Windows Forma Message Loop” section.
Here is one more link that explains the issue with nesting under the Nesting section.
I apologize for being verbose. Just wanted you guys to get a clear picture of my problem. Please let me know if you have any questions regarding the post. Any suggestions would be greatly appreciated.
EDIT:
We were able to resolve the issue, but still, it is a work-around. Here is what we did:
There were two ways to resolve this issue, but both were related to using static.
Static Winforms control:
We used the following static Winforms control
public static class ControlHolder
{
public static TextBox ReportFromDateTimeInstance;
}
In the OnChanged event of the "actual" control, we dump the actual control, ReportFromDateTime to the static control, ReportFromDateTimeInstance.
private void ReportFromDateTime_TextChanged(object sender, EventArgs e)
{
ControlHolder.ReportFromDateTimeInstance = (TextBox)sender;
}
And from then on, wherever we update the actual control (as in ResetDateTimeHandler method), we update the static control
private void ResetDateTimeHandler(bool cancelClicked)
{
ControlHolder.ReportFromDateTimeInstance = "Text changed";
}
This shows the updated value on the Front-End
Static EventAggregator
This work-around was provided by one of our colleague.
In this case, we are using our actual control, ReportFromDateTime, rather than the static control, ControlHolder.ReportFromDateTimeInstance
We used a static event aggregator for publishing/subscribing the ResetDateTimeEvent instead of using the Event Aggregator instance provided by Unity Container. So, instead of
eventAggregator.GetEvent<ResetDateTimeEvent>.Publish(true);
we used:
ResetDateTimeEvent.Instance.Publish(true);
And in the subscription:
ResetDateTimeEvent.Instance.Subscribe(ResetDateTimeHandler);
I know that we need not use a static event aggregator in this scenario since we are using the instance provided by Unity Container (which makes sure that a single instance is shared by all the ViewModels), but this also has resolved the issue.
So, I'm still confused on why the above two scenarios are solving the problem. Is it the static-ness that is solving the issue ?
As I was already saying, I feel that the controls are getting re-created, and by the time we have the controls in hand, they have been already re-created.
Any suggestions would greatly be appreciated.
I've done the same thing before in an app that had a WPF control inside of a WinForms control. The WPF used Prism/Unity (later switched to MEF) to start everything up. However, this created a whole new EventAggregator by default in the bootstrapper, so I had override the default IEventAggregator in the container with a static one b/c the WinForm side had already been created and was using its own IEventAggregator instance. The symptom was similar in that published events were not received.
In a mixed system such as yours, singletons are great for ensuring that everything's feeding off of the same reference, especially when your startup is in stages (WinForms then WPF).
Simple answer: yes, use singletons for shared references between WinForms code and WPF code. Those singletons can be fed into the container in the WPF bootstrapper so that injection still occurs in the WPF side, too.

Trouble with finding Automation Id for Border inside ItemTemplate

I have DataTemplate for a class which follows following hierarchy
I have given AutomationId to each of these Controls.
when I try to detect the highlighted border using Coded UI Test builder, i am unable to find it. whereas i am directly getting Checkbox inside one of its child control.
I am not able to automation to this parent control(Border) due to this problem.
If I place GroupBox instead of Border I am able to get this control.
posted actual datatemplate Here
please help out.
You should be able to use the Coded UI Test Builder's cross hairs to get to the level. Just point to it with the cross hairs, and when it points you to the checkbox, use the arrows to navigate the tree.
Another suggestion would be to use C# to manually add it to your map. This would be done by specifying an identifying property for the control. However, I think you may have trouble using AutomationProperties.Name or AutomationProperties.AutomationId. You'd have more luck adding an the Name property to the border and identifying the control with that.
So:
public HtmlControl BorderOrangeBorder
{
get
{
HtmlControl target = new HtmlControl([browser]);
target.SearchProperties["name"] = "OrangeBorder";
return target;
}
}

Directly addressing controls in a UserControl after adding TableLayoutPanel

I recently refactored the code of a user control in my WinForms project, and changed it from a user control with text boxes, combo and buttons that were just placed all over it, to a user control that now contains a TableLayoutPanel, which holds all the controls in a better order.
My problem is that in many places the code address the controls nested in the user control directly via the Controls dictionary - for example: MyUserControl.Controls["NameOfTextBox"].Visible = false;
Now, after I nested the text boxes and buttons in a TableLayoutPanel, I can't do such addressing anymore, and now I should write MyUserControl.Controls[0].Controls["NameOfTextBox"].Visible = false;, because otherwise I get an exception.
My question is whether I should change all my code in every place that addresses the contents of the user control, or can you offer me some workaround to implement on the user control itself, so when I'll try address the controls directly, it will forward it to the contents of the TableLayoutPanel.
Any Ideas?
My problem is that in many places the code address the controls nested in the user control directly...
That's probably the core issue. Try creating properties for you UserControl instead:
public bool NameBoxVisible {
get { return NameOfTextBox.Visible; }
set { NameOfTextBox.Visible = value; }
}
Then you can reference the controls directly in your UserControl but provide a separation of concern to the consumers of your control.

Creating Silverlight UserControl

I'm creating a silverlight user control that I should be able to drag and drop via blend. But this control needs to accept a map that is already on the page.
For eg.
Main.xaml contains a map control.
MapEditor.xaml contains buttons and other controls. In the .cs file, it needs to access a map control (the one in Main.xaml).
How do I go about getting this done?
I was thinking about adding a parameter in the contructor for MapEditor but how would I pass in the map as a parameter in design mode?
Thanks.
ps. I'm going to break out this control into a silverlight library so it could be used in multiple projects later.
You don't want to be giving your control a parameterised constructor, XAML will only construct types using their default constructor.
Simple Approach
The easiest approach would be to add DependencyProperty to your control to which you would assign the Map control (I'll use the type name MyMap in this example):-
public MyMap Map
{
get { return (MyMap)GetValue(MapProperty); }
set { SetValue(MapProperty, value); }
}
public static DependencyPropery MapProperty = new DependencyProperty("Map",
typeof(MyMap), typeof(MapEditor), new PropertyMetaData(null));
Now in Blend the Map property will appear in the Miscellaneous category in the Properties tab. You can then use the "Element Property" tab of the "Create Data Binding" to select the Map control to which it should bind.
Hard Core Approach
That said I would be inclined to build a proper customisable control following these guidelines Creating a New Control by Creating a ControlTemplate. With the addition that I would extend the ContentControl base class and include a ContentPresenter at the heart of the template. The control would make the assumption that the child control is a MyMap control.
This approach allows the entire appearance of the MapEditor control to be styled in Blend and it allows the Map control that is to be "edited" to be drap-drop onto the MapEditor as a child control.

WPF Prism V2 Using M-V-VM - Add a view at runtime to a region from the ViewModel

Hopefully quite a simple one, having my first try at WPF with Prism V2 using M-V-VM and so far finding everything pretty awsome. My Shell is pretty simple, Ribbon Control at the Top, DataGrid of Help desk tickets on the left, and a TabControl on the right.
When a user opens the selected ticket from the datagrid, I want the Ticket to open as a Tab on the Tab Control. I know in order to do that I need to add and then activate the View to the region using the RegionManager. But doing this from the ViewModel doesn't seem correct to me, although I could do it using DI (DepenecyInjection) it still rings alarms in my head about giving the ViewModel some knowledge about a View.
To Add to this, different modules will also be adding other views (Contact, Client etc) into the TabControl, I'd like to use DataTemplates to get the TabControl to display the View Correctly, can anyone give me any pointers for this as well.
Many Thanks
Ben
Full answers please, not just links. It's what StackOverflow is for!
MVVM + Services = Ultimate Power!
A service is just an interface that's well known and is registered in your IOC container. When the ViewModel needs to do something outside of itself, like say open a tabbed document, it uses the service. Then the service is implemented as needed for the particular program.
For example:
public interface IDocumentService
{
void OpenDocument(IViewModel viewModel);
}
internal class DocumentService:IDocumentService
{
public void OpenDocument(IViewModel viewModel)
{
// Implement code to select the View for the ViewModel,
// and add it to your TabControl.
}
}
{
// Somewhere in your ViewModel...
// Make sure you can get the IDocumentService
IDocumentService docService = ioc.Get<IDocumentService>();
docService.OpenDocument(new TicketViewModel());
}
Commands are the way to do this - you'll send a command to yourself, called "RequestBringTicketIntoView"; it will bubble up to the Window, where you handle it. Read Josh Smith's article:
http://joshsmithonwpf.wordpress.com/2008/03/18/understanding-routed-commands/

Resources