I'm using a ContentControl to render various UserControl derivations dynamically. I can't for the life of me figure out how to get the content to stretch when I resize the parent Window. I've found many references like this, but it's still not working for me. Here is a simple example:
This is the Window XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary Source="Dictionary1.xaml"/>
</Window.Resources>
<Grid>
<ContentControl VerticalAlignment="Top"
HorizontalAlignment="Left"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Content="{Binding Path=ChildView}"
Margin="10"/>
</Grid>
</Window>
This uses the resource file Dictionary1.XAML:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels ="clr-namespace:WpfApplication3"
xmlns:views ="clr-namespace:WpfApplication3">
<DataTemplate DataType="{x:Type viewModels:UserControlViewModel}">
<views:UserControl1/>
</DataTemplate>
</ResourceDictionary>
Here is the code behind for the main Window as well as the view model classes:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
public class MainViewModel
{
public UserControlViewModel ChildView { get; set; }
public MainViewModel()
{
ChildView = new UserControlViewModel();
}
}
public class UserControlViewModel
{
}
and finally the user control:
<UserControl x:Class="WpfApplication3.UserControl1"
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"
mc:Ignorable="d"
Background="Blue"
Height="141" Width="278"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<Grid>
</Grid>
</UserControl>
Here's what it looks like at run time:
What am I missing here? How can I get the child content to behave such that it remains anchored to the top/left of the parent and the bottom/right stretches as the parent is resized?
Two things:
First, you want to remove the VerticalAlignment and HorizontalAlignment on your ContentControl. Setting these will prevent the content control from stretching within its container, so Width and Height are respected, which are both zero by default (so the container has no size on its own).
Setting VerticalAlignment and HorizontalAlignment to Stretch, or leaving it out since it’s the default, will make the container fill the grid, which is what you want.
<ContentControl Content="{Binding Path=ChildView}" Margin="10" />
Second, setting Width and Height within the UserControl will set its size to that fixed size, so it will not adjust itself. Remove those attributes and the user control will default to stretch too, making it fill the content control.
If you want to have a certain size for design purposes, then set the design size instead of the actual control size. For that, you have the d XAML namespace which contains DesignWidth and DesignHeight properties. Setting these will affect the designer but they are ignored later when the view is rendered.
<UserControl x:Class="WpfApplication3.UserControl1"
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" mc:Ignorable="d"
d:DesignWidth="400" d:DesignHeight="250"
Background="Blue">
…
</UserControl>
You set the Height and Width property of the UserControl. This removes any leeway the WPF layouting has. So it does the best thing it can, which is centering the UserControl. If you remove the width and height, it should stretch as you expect.
<UserControl x:Class="WpfApplication3.UserControl1"
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"
mc:Ignorable="d"
Background="Blue"
Height="141" Width="278" //<-- remove
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<Grid>
</Grid>
</UserControl>
As poke kindly reminded me, you also have to remove VerticalAlignment="Top" and HorizontalAlignment="Left"
<ContentControl VerticalAlignment="Top" //<--remove
HorizontalAlignment="Left" //<--remove
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Content="{Binding Path=ChildView}"
Margin="10"/>
Related
Let's create an empty WPF project.
Add a very simple UserControl (i named it MyUserControl):
<UserControl x:Class="Delete_This_Test.MyUserControl"
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"
mc:Ignorable="d"
Background="Red"
Height="100">
<Grid>
</Grid>
</UserControl>
As you can see, i have changed only 2 properties: Background and Height to "Red" and "100".
Put our created control in MainWindow:
<Window x:Class="Delete_This_Test.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:Delete_This_Test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:MyUserControl Width="100"
Height="200"
Background="Blue">
</local:MyUserControl>
</Grid>
</Window>
Here, i have changed Width, Height and Background to "100", "200" and "Blue".
And it works: Without ControlTemplate Picture
But if we put MyUserControl in some ControlTemplate, for example of Button:
<Window x:Class="Delete_This_Test.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:Delete_This_Test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Button>
<Button.Template>
<ControlTemplate TargetType="Button">
<local:MyUserControl Width="100"
Height="200"
Background="Blue">
</local:MyUserControl>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Window>
This will not work. Only Width property will changed, because we didn't set it in MyUserControl xaml.
Height and Background will be the same as "100" and "Red":
With ControlTemplate Picture
So my question is: Is it bug of WPF, or i'm missing something obvious?
*
Because i need to use one custom control in different templates, and change some properties, e.g. Background of control
When using Margins to specify top/left/bottom/right positions, the location of my button and inner grids is not what I expected with the following xaml:
<Window
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"
mc:Ignorable="d" x:Class="WpfApplication1.MainWindow"
Title="Dialog" WindowStartupLocation="CenterScreen" ResizeMode="CanResize" Height="399" Width="590">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 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:sys="clr-namespace:System;assembly=mscorlib" x:Name="TopÆLeftÆRight596" Background="#FFFFFF">
<Grid Margin="150, 85, 153, 110" x:Name="Panel597" Background="#EEEEEE">
<Button Margin="97,77,108,101" x:Name="BLANK_BUTTON598" ToolTip="Blank" Cursor="Arrow" FontFamily="Arial" FontSize="12" FontWeight="Normal" Foreground="#000000" Content="Blank" />
</Grid>
</Grid>
I believe what is happening is that because my container grid is set to stretch (x:Name="TopÆLeftÆRight596"), the margins are calculated off the window which includes the title bar. If I set height and width on that grid, resizing of the inner controls is no longer possible.
I need a way to specify the height/width values on the window, or perhaps another type of container, so that the margin calculations do not include the title bar, and the inner contents grow and shrink when the window is resized.
Please keep in mind I have no control over the left/top/right/bottom margin values provided.
In essence, what I'm trying to do is duplicate top/left/bottom/right anchoring of the Grid(x:Name="Panel597") and Button(x:Name="BLANK_BUTTON598") controls.
Any help is greatly appreciate. Thanks.
I created Wpf UserControl and am hosting it in WinForm.
<UserControl x:Class="Sapphire.WpfUserControl"
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"
mc:Ignorable="d" Height="527" Width="992">
<Canvas x:Name="videoCanvas" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" >
<Label Canvas.ZIndex="2" Content="Label" Canvas.Left="165" Canvas.Top="50" Width="125" Foreground="#FFFFFEFF"/>
<MediaElement x:Name="videoElement" Canvas.ZIndex="1" Canvas.Left="10" Canvas.Top="10" />
</Canvas>
As shown in designer file this WPF control is hosted through HostElement:
//
// elementHost1
//
this.elementHost1.Dock = System.Windows.Forms.DockStyle.Fill;
this.elementHost1.Location = new System.Drawing.Point(0, 0);
this.elementHost1.Name = "elementHost1";
this.elementHost1.Size = new System.Drawing.Size(1130, 593);
this.elementHost1.TabIndex = 2;
this.elementHost1.Text = "elementHost1";
this.elementHost1.Child = this.wpfUserControl1;
So it looks all correct. You also can see that the DockStyle is Fill.
However, the WPF control does not fill the entire WinForm and always shows up of a size as set and displayed in Designer.
I removed the Height and Width both from Canvas and from MediaElement that Canvas contains but it did not have any effect...
I'd appreciate if somebody can point out what I am doing wrong here - I am new to WPF.
You need to remove the Width and Height of the <UserControl> so that the containing ElementHost controls the size of the contained elements:
<UserControl x:Class="Sapphire.WpfUserControl"
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"
mc:Ignorable="d">
If you want a specific size in the designer, you can use d:DesignHeight and d:DesignWidth attributes:
<UserControl x:Class="Sapphire.WpfUserControl"
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"
mc:Ignorable="d" d:DesignHeight="527" d:DesignWidth="992">
I made a user control
<UserControl x:Class="MyApp.MyControl"
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"
mc:Ignorable="d" x:Name="uc">
<Grid Width="Auto" Height="Auto">
<TextBlock Text="{Binding Path=DataContext.TextContent, ElementName=uc}"/>
<TextBlock Text="{Binding Path=DataContext.TextContent2, ElementName=uc}"/>
</Grid>
I want the sub-controls in the defined control(uc) will bind to the properties of uc.DataContext. I used the defined control as follows:
<Window x:Class="Tms.TMSClient.Views.MainWindow" Name="window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:control="clr-namespace:MyApp"
xmlns:ribbon="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary">
<control:MyControl DataContext="{Binding Path=MyControlVM}"/>
The DataContext which is assigned to the window has this structure: WindowVM.MyControlVM.TextContent.
The given code does not work because the textbox's DataContext is bound to WindowVM instead. I think the problem may be because the inner textbox is bound before the defined control (uc) is, thus the bounded DataContext for uc does not take effect yet.
What I want is: the custom control (MyControl) will be bound to its corresponding viewmodel (MyControlVM), and the inner elements of MyControl will be bound to the properties of MyControlVM.
Do you have any solutions for this problem?
If I understand you correctly, you want to data bind a property from your MyControl view model to a TextBox.Text property inside the MyControl UserControl. If that is correct, then you can use a RelativeSource Binding, or the ElementName syntax that you are already using.
First, make sure that your view model is set as the DataContext for the UserControl:
public MyControl()
{
DataContext = new YourControlViewModel();
}
As child controls automatically inherit their parent's DataContext objects, you can now reference this view model from the TextBox through the MyControl.DataContext property from the UserControl's XAML:
<TextBlock Text="{Binding DataContext.TextContent,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
That's all you need.
<TextBlock Text="{Binding Path=TextContent}"/>
works for me in my test-application.
MainWindow.xaml
<Window x:Class="DataContextTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:DataContextTest"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<my:MyOuterDataContext />
</Window.DataContext>
<Grid>
<my:MyControl DataContext="{Binding Path=MyInnerDataContext}" />
</Grid>
MyControl.xaml
<UserControl x:Class="DataContextTest.MyControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding Path=TextContent}" />
</Grid>
DataContexts:
public class MyOuterDataContext
{
public MyInnerDataContext MyInnerDataContext { get; set; }
public MyOuterDataContext()
{
MyInnerDataContext = new MyInnerDataContext();
}
}
public class MyInnerDataContext
{
public string TextContent { get { return "foo"; } }
}
By default every control inherits its DataContext from its parent control. Thus there is no need to explicitly bind to it.
Indeed, when you want to bind a control's DataContext to a nested property then you have to specifiy this:
<control:MyControl DataContext="{Binding Path=TextContent}"/>
I'm learning WPF at the moment. I'm finding xaml quite tough to use. I have MainWindow.xaml defined like this:
<Window x:Class="Compliance.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary Source="MainWindow.Resources.xaml"></ResourceDictionary>
</Window.Resources>
<Grid>
</Grid>
</Window>
And MainWindow.Resources.xaml like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Compliance.ViewModel"
xmlns:vw="clr-namespace:Compliance.View">
<DataTemplate DataType="{x:Type vm:Entities.AbstractEntityViewModel}">
<vw:AbstractEntityView></vw:AbstractEntityView>
</DataTemplate>
</ResourceDictionary>
AbstractEntityView is like this:
<UserControl x:Class="Compliance.View.AbstractEntityView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<Label Content="ID:"></Label>
<TextBlock Text="{Binding Path=EntityId}"></TextBlock>
</StackPanel>
</UserControl>
Then in App.xaml.cs I am overriding OnStartup like this:
MainWindow window = new MainWindow();
//Model class
Individual ind = new Individual(1,"Name");
//subclass of AbstractEntityViewModel
var vm = new Entities.IndividualEntityViewModel(ind);
window.DataContext = vm;
window.Show();
However, nothing appears in the window.
I used the answer from this question to get my control to render. However, this requires you to refer to elements in the view from the code, which I don't want to do.
Is it possible to get a window to pick a View to render based on the ViewModel set as its datacontext? Or do I have the wrong idea about how MVVM is supposed to work?
You have the right idea, but you're not actually telling WPF to display your ViewModel anywhere
I usually host a ViewModel in a ContentControl object if I am binding to a single ViewModel
<Window x:Class="Compliance.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary Source="MainWindow.Resources.xaml"></ResourceDictionary>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding }" />
</Grid>
</Window>
The ContentControl is usually not needed for lists of Models or ViewModels, since the object is automatically inserted as the Content property of the ContentPresenter of each item. For example, no ContentControl is needed when binding a ListBox to a collection of ViewModels
<ListBox ItemsSource="{Binding MyCollectionOfViewModel}" />