How do I load user controls dynamically? - wpf

How can I load a user control[s] in a window dynamically (using code at runtime)?

I'd highly recommend having a look at Prism, since composite user interfaces is what it's for. However, since this would require you refactoring your entire application, I'll also answer your question directly.
If you want a single user control in a container, put a ContentControl in your XAML and then set the Content property. If you are using a view model, you could bind Content to a FrameworkElement property on the view model:
contentControlInstance.Content = new CustomUserControl();
If you want multiple controls in a list, use an ItemsControl and assign an ObservableCollection<> to the ItemsSource property. If you are using a view model, you could bind ItemsSource to an ObservableCollection property on the View Model.
Then you can just add/remove views from that ObservableCollection:
private ObservableCollection<FrameworkElement> views =
new ObservableCollection<FrameworkElement>();
private void Initialize()
{
itemsControl.ItemsSource = views;
}
private void AddView(FrameworkElement frameworkElement)
{
views.Add(frameworkElement);
}

For adding multiple controls you need container.
Suppose you have a StackPanel container "myStack"
<Window ..>
<StackPanel Name="MyStack" />
</Window>
You can create control dynamically and add it to container. See code below
void AddButtons()
{
Button B1=new Button(),B2=new Button(), B3=new Button();
B1.Content="Hello";
B2.Content="First";
B3.content="Application";
// Now you can set more properties like height, width, margin etc...
MyStack.Children.Add(B1);
MyStack.Children.Add(B2);
MyStack.Children.Add(B2);
}

Or use binding. Here's a really crude example showing how different WPF controls can be shown in a single WPF window using ContentControl and binding (which is what a toolkit like Prism or Caliburn Micro does).
XAML:
<UserControl x:Class="ViewA">
...
<UserControl/>
<UserControl x:Class="ViewB">
...
<UserControl/>
Code:
void ShowViewModelDialog (object viewModel)
{
var host = new MyViewHost();
FrameworkElement control = null;
string viewModelName = viewModel.GetType().Name;
switch (viewModelName )
{
case ("ViewModelA"):
control = new ViewA();
break;
case ("ViewModelB"):
control = new ViewB();
break;
default:
control = new TextBlock {Text = String.Format ("No view for {0}", viewModelName);
break;
}
if (control!=null) control.DataContext = viewModel;
host.DataContext = control;
host.Show(); // Host window will show either ViewA, ViewB, or TextBlock.
}

Related

WPF/MVVM windowsservice without viewmodel reset

I'm want to remove from my view model's creating of view's
I wrote WinodwsService class to creating a new window:
public class WindowService : IWindowService
{
public void ShowWindow(object viewModel)
{
//var win = new DXWindowCloasable(viewModel);
var win = new DXWindow();
win.Content = viewModel;
win.DataContext = viewModel;
win.ShowDialog();
}
}
In view model I call method:
var vm = new PolaPrzewoznikowViewModel(konf);
IWindowService wnf = new WindowService(); // this is only for test
wnf.ShowWindow(vm);
In UserControl I have defined view model type:
<UserControl.DataContext>
<local:PolaPrzewoznikowViewModel />
</UserControl.DataContext>
When I have this, I can drill down (CTRL + B) on commands, and user an code completition when I'm projecting a View - this is very helpful.
But... when I use
win.ShowDialog(); the new view model is created. And displayed view has view model without parameters (default constructor).
How can I use window service and keep defined UserControl.DataContext in view?
instead of initializing DataContext in xaml
<UserControl.DataContext>
<local:PolaPrzewoznikowViewModel />
</UserControl.DataContext>
I suggest to use DesignInstance:
<UserControl d:DataContext="{d:DesignInstance Type=local:PolaPrzewoznikowViewModel,
IsDesignTimeCreatable=True}" ...>
It will give IntelliSense and designer enough information in design-time, but a new instance won't be created in run-time (there will only DataContext from WindowService)
Why are you setting both the content and the datacontext of the window?
Regarding intellisense you should do as ASh suggests, the data context by its nature will be available to all view descendants.
If you don't want to implement the window service yourself you can always use my framework https://github.com/FantasticFiasco/mvvm-dialogs.

Custom Usercontrol with MVVM and Catel

I've created a custom usercontrol that's composed of a AutoCompleteBox with a Selected Item... till now I've implemented it in a way I don't like... I mean I've a XAML view, a Viewmodel and in the viewmodel I load data from a stored procedure.
Since the AutoComplete box is a third party UserControl I've added it to the XAML view and not defined as a custom usercontrol. What's the best practice to do so?
I think the fact that I'm using Catel as MVVM Framework is irrilevant right now..
Thanks
UPDATE #1
My usercontrols need to have some properties that are passed via XAML for example (LoadDefaultValue)
<views:PortfolioChooserView x:Name="PortfolioChooserView" DataContext="{Binding Model.PortfolioModel}" Height="25" LoadDefaultValue="True" Width="150" />
To achieve such a scenario I had to define a dependency property in my PortfolioChooserView defined as
public bool LoadDefaultValue
{
get { return (bool)GetValue(LoadDefaultValueProperty); }
set { SetValue(LoadDefaultValueProperty, value); }
}
public static readonly DependencyProperty LoadDefaultValueProperty = DependencyProperty.Register(
"LoadDefaultValue", typeof(bool), typeof(PortfolioChooserView), new PropertyMetadata(default(bool)));
Since if I would have defined it in Viewmodel only I wouldn't have been able to set it.
The odd thing is that in order to pass it to the viewmodel I had to do such a trick
public PortfolioChooserView()
{
InitializeComponent();
if (!isFirstLoad) return;
Focusable = true;
PortfolioCompleteBox.AllowDrop = true;
PortfolioCompleteBox.Focus();
DragDropManager.AddPreviewDragOverHandler(PortfolioCompleteBox, OnElementDragOver);
DragDropManager.AddDropHandler(PortfolioCompleteBox, OnElementDrop);
DataContextChanged += PortfolioChooserView_DataContextChanged;
isFirstLoad = false;
}
void PortfolioChooserView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var dataContext = DataContext as PortfolioModel;
if (dataContext != null)
{
dataContext.LoadDefaultValue = LoadDefaultValue;
dataContext.AllowNull = AllowNull;
//var converter = new PortfolioConverter();
//var portfolio = (Portfolio) converter.Convert(SelectedItem, null, null, CultureInfo.CurrentCulture);
//dataContext.SelectedItem = portfolio;
}
}
But I really dislike to use the DataContextChanged event ...do you see a better approach?
Thank
UPDATE#2
I keep this toghether since It's a related question...
On some viewmodel I used DeferValidationUntilFirstSaveCall = true; in the Constructor to disable the validation at load but my custom usercontrols shows the red border around... what should I do to propagate that info to the nested usercontrols?
Thanks again
See Orc.Controls for tons of examples. It's an open-source library that has a lot of user controls built with Catel, even one with an auto complete box.

How do I make user controls for both ListView and ListViewItem work with each other?

I have all the styling, triggers, etc. down for ListView and ListViewItem, and I want to turn them into user controls. How do I make sure that these two "match up" with each other, so that MyListView accepts MyListViewItems as content? Also, considering that I must end the ListView tag by the end of the user control XAML file, I am not sure how I would add items to it.
If you want them to be reusable with different data sets, especially through binding, you should stay away from UserControls and just make custom controls derived from the original types. In that case you create a standalone MyListView.cs and MyListViewItem.cs and all of the XAML for the controls goes into default Styles (usually also containing a ControlTemplate) in Themes/Generic.xaml. You can see an example of this setup by just adding a WPF Custom Control to your WPF project from Add New Item.
Once you've created the .cs files for your custom controls you just need to override a few methods from the base ItemsControl to use MyListViewItem as the item container control. The ListView would end up like this:
public class MyListView : ListView
{
static MyListView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyListView), new FrameworkPropertyMetadata(typeof(MyListView)));
}
protected override DependencyObject GetContainerForItemOverride()
{
return new MyListViewItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is MyListViewItem;
}
}
You can now use your custom MyListView exactly as you would a normal ListView, including binding to ItemsSource.
Inheritance should take care of that for you. In other words, if you have two user controls, the first one with a basic element of ListView (not UserControl) and the other of ListViewItem (again, not UserControl), and you make sure they extend ListView and ListViewItem respectively in the .cs code, the following should work equally:
ListView lv = new ListView();
lv.Items.Add(new ListViewItem());
or
MyListView mlv = new MyListView();
mlv.Items.Add(new myListViewItem()); //If your myListView extends ListView, and myListViewItem extends ListViewItem in your user control files, of course
In case you are looking for a XAML solution, you should import your namespace at the top
xmlns:myControls="WhateverYourNamespaceAndAssemblyAre"
and on you page/window/whatever
<myControls:myListView>
<myControls:myListViewItem/>
<myControls:myListViewItem/>
</myControls:myListView>

retrieve usercontrol from itemscontrol item

i'm new to wpf and i am having a problem with items control. What i want to do is that i want to retrieve the user control that i've added in itemtemplate of items control. I tried using LoadContent() method of DataTemplate but it returns me the default template.
Here my code
ItemsControl parent = FindParent<ItemsControl>( this );
//this.isEditMode = true;
//this.editIngLayer.Visibility = Visibility.Visible;
foreach( var container in parent.Items )
{
DependencyObject contentPresenter=
parent.ItemContainerGenerator.ContainerFromItem( container ) as ContentPresenter;
//Something to retrieve the usercontrol
MyUserControl uC=contentPresenter.GetControl();
//
}
Thanks.
If you have your ItemsControl item with you then you can iterate its Visualtree to reach to your usercontrol using VisualTreeHelper
Recursive find child is explained in this post
How can I find WPF controls by name or type?

Can I add a DependencyProperty on an windows user control?

I'm trying to host a Visio ActiveX object in a WPF application.
To do this, I created a Windows user control project where I add the Visio object. This windows user control is then hosted on an WPF user control in an WindowsFormsHost object.
<WindowsFormsHost Name="wfHost" Grid.Row="1">
<wf:VisioUserControl FileNamePath="?"/>
</WindowsFormsHost>
What I would like to do is to bind the value of the FileNamePath member to the value of a TextBox element which defines the path.
The project follows the MVVM pattern, so there is no way that I can access the VisioUserControl object in my ViewModel.
The solution I was thinking about is to bind the FileNamePath member to the value of the TextBox that contains the path, but it is not a DependencyProperty and it seems that I'm not able to define one in the code behind of the windows user control.
So, is there any workaround to perform this binding?
Thanks in advance.
You can solve this by creating a UserControl that wraps your VisioUserControl (I wrote a simple tutorial on UserControl creation here). You can then add a FileNamePath dependency property to your UserControl. In the property changed handler of this dependency property, set the FileNamePath property on the VisioUserControl that this user control wraps.
Ok I have created an example of a WPF usercontrol that is hosting a Winforms control, with a dependency property that is bound to the winforms control's text property.
public partial class ActiveXObjectHoster : UserControl
{
private static System.Windows.Forms.Label testObject;
public ActiveXObjectHoster()
{
InitializeComponent();
testObject = new System.Windows.Forms.Label();
windowsFormsHost1.Child = testObject;
}
#region Properties
public static DependencyProperty FileNameProperty = DependencyProperty.Register("FileName", typeof(string), typeof(ActiveXObjectHoster), new UIPropertyMetadata("",new PropertyChangedCallback(OnFileNamePropertyChanged)));
public string FileName
{
get { return (string)GetValue(FileNameProperty); }
set
{
SetValue(FileNameProperty, value);
}
}
private static void OnFileNamePropertyChanged(
DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
testObject.Text = (string)e.NewValue;
}
#endregion
}
Here is the xaml of the control (its very simple)
<UserControl xmlns:my="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
x:Class="WPFTestApp2.Controls.ActiveXObjectHoster"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="ObjectHost"
Height="100" Width="100">
<Grid>
<my:WindowsFormsHost x:Name="windowsFormsHost1" />
</Grid>
</UserControl>
What you need to do is change the test object from a Label to whatever Visio object you were using. Then in the property callback change the text property to the filename or whatever property you wanted.
As mentioned above this is done in the code behind, but that is fine for a user control, its completely decoupled from whatever thing is using it, you just need to bind to the filename property of the control.
Here is a link to a project I created showing how the control is used. There is a textbox whos text is bound to the FileName property, which changes the Winforms Labels text.
You can place this in a Winforms Usercontrol if you want to use it in winforms (like you mentioned in your reply to my comment)
Try replacing the label for your control and see if it works.
Why not implement a UserControl to wrap the WindowsFormHost and the Visio user control? Then you cann add a Dependency Property, and implement in the code behind a handler for the PropertyChangedCallback, and appropiately interact with the WinForms control

Resources