I have a UserControl (UserControlA) that holds multiple other UserControls (UserControlBs), but I'd like that number to be dynamic.
Currently, the XAML for UserControlA looks like this:
UserControlA.xaml
<UserControl ...>
<StackPanel>
<local:UserControlB />
<local:UserControlB />
<local:UserControlB />
</StackPanel>
</UserControl>
And then UserControlA shows up, as expected, in MainWindow.xaml when I do this:
MainWindow.xaml
<Window ...>
<Grid>
<controls:UserControlA />
</Grid>
</Window>
However, instead of being stuck with just 3 UserControlBs I'd like for that number to be dynamic based on an object I pass to it. Something like this...
<Window ...>
<Grid>
<controls:UserControlA UserControlBs="{Binding Path=UserControlBList}" />
</Grid>
</Window>
And then, depending on how many UserControlBs there are, the UserControlBs are created and populate UserControlA.
Any good examples on how to do this?
Thanks!
Related
I'm building a WPF application and using caliburn.micro for MVVM.
I'm having dozens of views (UserControls).
The views has a header body and footer.
As shown in below image, header contains two buttons for crud operation, and the footer contains a status bar.
The header and footer parts will be same for all views, but the body contents will be different for view to view.
Currently I'm having repeated code for header and body for each view, and now I'm trying to eliminate repeating code.
To achieve this I'm thinking to make a common base view, to share with all other views.
Current Implementation
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Grid Name="Header/>"
<Grid Name="Body/>"
<Grid Name="Footer/>"
</StackPanel>
</UserControl>
Trying to acheive something like
Base View
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Grid Name="Header/>"
<!-- {Placeholder for child view} -->
<Grid Name="Footer/>"
</StackPanel>
</UserControl>
ChildView
<Grid Name="Body"/>
May be my approach is wrong (I'm bit new to WPF).
My goal is to eliminate the repeating code, by inheriting some controls into the view.
How can I combine base view with child view?
Could anyone advice me to achieve my requirement?
Providing some code example will be highly appreciated.
You could for example define the common header and the footer in the parent window, or in two separate user controls that you create in the XAML markup of the parent window, and then inject the child views into the same window using a ContentControl, e.g.:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300">
<StackPanel>
<local:HeaderUserControl />
<ContentControl Content="{Binding CurrentViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:ViewModelA}">
<local:ViewA />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelB}">
<local:ViewB />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
<local:FooterUserControl />
</StackPanel>
</Window>
The child views doesn't know anything about the header and the footer.
You can nest your UserControls just like you can nest any other element so you could do something like this
<UserControl x:Name="HeaderControl">
<UserControl x:Name="ChildControl"/>
</UserControl>
Then you'd use a DependencyProperty in your header control to associate your child control with it something like this
public UserControl ChildControl
{
get { return (UserControl)GetValue(ChildControlProperty); }
set { SetValue(ChildControlProperty, value); }
}
public static readonly DependencyProperty ChildControlProperty =
DependencyProperty.Register("Text", typeof(UserControl), typeof(UserControl), new PropertyMetadata(null));
This article gives a nice overview - I know it says Silverlight but it uses the same basic approach.
I created a "UserControl" based on a TextBox. That means I created a new UserControl and replaced UserControl by TextBox in xaml and xaml.cs files.
Now I want that my new TextBox control shall have a popup to display some suggestions.
Now my question is: Where can I define the look/structure of the Popup as XAML? The Popup definition shall be part of the newTextBox.
That's what I have:
<TextBox x:Class="WpfApplication11.UserControl2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApplication11"
mc:Ignorable="d"
MaxWidth="{Binding Path=ActualWidth}">
</TextBox>
I think you're totally missing what a UserControl actually is. To put it simply, a UserControl is a group or collection of controls that make up one larger... uh... control.
I want that my new TextBox control shall have a popup to display some suggestions.
What you are describing here is a UserControl. You cannot place a Popup inside a TextBox.
So. What you're after here is probably something like this:
<UserControl ... >
<Grid>
<TextBox Name="txt"
Width="150" ... />
<Popup PlacementTarget="{Binding ElementName=txt}"
Placement="Bottom"
IsOpen="True"
StaysOpen="True"
Width="{Binding ActualWidth, ElementName=txt}">
<!-- Some popup content here -->
</Popup>
</Grid>
</UserControl>
This is obviously an extremely simplified example, but you get the idea.
Well i have a little resource dictionary and a little class which manages the code behind, here is the code:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyResources">
<DataTemplate x:Key="Template1">
<TextBox Name = TB_1 textchanged="handle_text" Margin="10,50 />
<Button Name="BT_1" Content="test1" Margin="10,10" />
</Grid>
</DataTemplate>
</ResourceDictionary>
The dataTemplate is displayed in a other class in a contentcontrol, that runs properly.
But in the class MyResources, i would like to get the text value of the textbox TB_1 with the handle_text function when the event textchanged is risen but it seems that the class MyResources doesn't see the variable textbox with the name TB_1 (and all others variables).
Is it possible to do this?
I give you thanks for your help.
My WPF application uses a Resource Dictionary. I'm also using MVVM.
I am binding to a ResourceDictionary, but want to bind my MainWindow ViewModel to the MainWindow (of type Window) but MVVM won't let me as MainWindow it's not type UserControl.
<Grid.Resources>
<ResourceDictionary Source="Resources\ResourceDictionary.xaml" />
</Grid.Resources>
<Grid.DataContext>
<Binding Source="{StaticResource Mwvm}" />
</Grid.DataContext>
</Grid>
This means I can't do this
<DataTemplate DataType="{x:Type viewModel:MainWindowViewModel}">
<root:MainWindow x:Key="Mwvm" />
</DataTemplate>
Does any one know how I can do the same thing but when the object is a Window and only using XAML (I know I can do this with code behind in the app.xaml onstartup())?
EDIT
To make this very clear, I know that within my MainWindow I can declare a namespace to my ViewModel, but is this the correct way when the namespace is already referenced in my ResourceDictionary and I'm referencing my ResourceDictionary.
Uhm how about?
<Window>
<Window.DataContext>
<someNs:YourVmClass /> <!-- calls the empty c_tor of the class-->
</Window.DataContext>
</Window>
(I'm not sure, if I understood your question. But I guess, that's what you really want.)
According to your edit:
Sure you could do something like
<!-- Define an instance of your VM in the ResourceDictionary -->
<ResourceDictionary>
<someNs:YourVmClass x:Key="InstOfYourVmClass" />
</ResourceDictionary>
In your view you could do something like this.
<Grid>
<Grid.Resources>
<ResourceDictionary Source="Resources\ResourceDictionary.xaml" />
</Grid.Resources>
<Grid.DataContext>
<StaticResource ResourceKey="InstOfYourVmClass" />
</Grid.DataContext>
</Grid>
But I would strongly recommend not to choose this approach. Problem is, everytime you're referencing this ResourceDictionary the current instance InstOfYourVmClass will be overwritten by a new instantiated version.
How can I set the DataContext on my Grid in XAML, instead of in the constructor?
Here is how I do it in the constructor (LayoutRoot is the XAML Grid defined in the XAML):
this.LayoutRoot.DataContext = this.HPVM;
I would prefer to do it right in the XAML, but I do not know how to reference the HPVM object in XAML. HPVM is a public property on the USerControl class.
It works fine as listed above, but again, I just want to know how to properties of the UserControl class in XAML, rather than always having to do it in code.
Here is all the relevant code:
<UserControl x:Class="SilverlightApplication1.SLHolePattern" x:Name="HolePatternsControl"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls"
xmlns:local="clr-namespace:SilverlightApplication1"
xmlns:GeoPatterns="clr-namespace:GeoPatterns"
Height="700">
<UserControl.Resources>
...
And here is my constructor where the DataContext is currently set:
namespace SilverlightApplication1
{
public partial class SLHolePattern : UserControl, INotifyPropertyChanged
{
public HolePatternsViewModel HPVM;
public SLHolePattern()
{
InitializeComponent();
this.HPVM=new HolePatternsViewModel();
this.LayoutRoot.DataContext = this.HPVM;
...more code here
}
It all works fine, but I just want to learn how to set the DataContext in XAML, not in code.
The answer Chris gave works just fine.
I have tested and it worked for me.
You can instantiate your class in XAML (within the UserControl.Resources) and
then bind the datacontext to a static resource.
Follow code:
<UserControl ...>
<UserControl.Resources>
<myNS:MyClass x:Name="TheContext" x:Key="TheContext"></myNS:MyClass>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource TheContext}" >
<TextBlock Text="{Binding Path=Field1}">
</TextBlock>
</Grid>
</UserControl>
The following monstrosity works in Silverlight 4
<UserControl
DataContext="{Binding HPVM, RelativeSource={RelativeSource Self}}">
<UserControl.DataContext>
<vm:ThisUCViewModel />
</UserControl.DataContext>
try something like this.....
<Grid DataContext="{Binding Path=HPVM}">
</Grid>
where HPVM is a public member of this--> your form etc.
Create the instance of your class in the xaml, by adding something like this to your resources section.... (don't forget to add your xmlns namespace)
<my:bogart x:Key="franken"/>
then, bind the data context to the static resource you just added....
<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource franken}">
<TextBox Background="Red" Foreground="White" Text="{Binding Path=sum}" />
</Grid>
In Silverlight 4, I was able to get this working by doing the following:
Give the Page/UserControl an x:Name="myPage"
In your control binding use normal Element bidning syntax. In my case I want to bind to an observable collection of objects in my code behind for my ItemsSource property:
<ComboBox
ItemsSource={Binding ElementName=myPage, Path=MyObservableObjectList, Mode=TwoWay}
I haven't tried this with DataContext but know you can do element to element binding for DataContext as I do this for Grids whose context is based on the selected item of some other drop down on the page.
This is not possible (It is possible in WPF with {Binding RelativeSource={RelativeSource Self}}, but Silverlight is more limited.
You have to do it through code.
<UserControl.Resources>
<ResourceDictionary>
<vm:YourModelx:Key="myModel"/>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource myModel}"/>
</UserControl.DataContext>