I am workign on MVVM, and i have Main View in which i have a tabcontrol and 3 tabitems. Now on click to each tabitems i want to display a new view. (I have three Views).
My try to do that is :
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm1="clr-namespace:WpfApplication1"
xmlns:vm2="clr-namespace:WpfApplication1"
xmlns:vm3="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<vm1:View1 x:Key="View1Display1"></vm1:View1>
<vm2:View2 x:Key="ViewDisplay2"></vm2:View2>
<vm3:View3 x:Key="ViewDisplay3"></vm3:View3>
</Window.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<TabControl Background="Green">
<TabItem Height="20" Width="100" Header="Tab1" DataContext="{Binding Path=View1Display1}"></TabItem>
<TabItem Height="20" Width="100" Header="Tab2" DataContext="{Binding Path=View1Display2}"></TabItem>
<TabItem Height="20" Width="100" Header="Tab3" DataContext="{Binding Path=View1Display3}"></TabItem>
</TabControl>
</Grid>
</Window>
Where View1 is : (Similar is View2 and View3)
<UserControl x:Class="WpfApplication1.View1"
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 VerticalAlignment="Center" HorizontalAlignment="Center">I am from View1</TextBlock>
</Grid>
</UserControl>
MainWindow.xaml.cs is :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
This is ViewModel:
class ViewModel
{
public ViewModel()
{
}
}
Why TabItem do not show the respective views on clicking to them even i have set datacontext corresponding to their View.
Instead of binding the DataContext on each TabItem, you need to bind the Content, and then you can bind each TabItem to an instance of its ViewModel, of Do that in the UserControls them selfs
<TabControl Background="Green">
<TabItem Height="20" Width="100" Header="Tab1" Content="{StaticResource View1Display1}"></TabItem>
<TabItem Height="20" Width="100" Header="Tab2" Content="{StaticResource ViewDisplay2}"></TabItem>
<TabItem Height="20" Width="100" Header="Tab3" Content="{StaticResource ViewDisplay3}"></TabItem>
</TabControl>
since your views are defined as static resource use StaticResource instead of binding
Related
I have a data grid in WPF as shown below . I need to pass this as an object of Type Visual class in the code behind . Is that possible in WPF
A DataGrid is a Visual so you only need to assign the element an x:Name in your XAML markup:
<DataGrid x:Name="dg" ItemsSource="{Binding Items}" AutoGenerateColumns="False" >
...
You could then refer to it using this name in the code-behind as the XAML compiler creates a backing field for you:
Visual v = dg;
Yes, you can.
Xaml:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<DataGrid x:Name="MyGrid" ItemsSource="{Binding Items}" AutoGenerateColumns="False" Grid.Row="1">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Image" Width="SizeToCells" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Image}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Grid.Background>
<VisualBrush Visual="{Binding Visual}"/>
</Grid.Background>
</Grid>
</Window>
And here is what you can do in the backend code. Simplay assign the control to Visual.
public class VM
{
public Visual Visual { get; set; }
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new VM {Visual = MyGrid};
}
}
It is also possible to directly reference in the xaml using
<VisualBrush Visual="{Binding ElementName=MyGrid}"/>
I have a very simple usercontrol:
<UserControl x:Class="PointOfSale.UserControls.HousesGrid"
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">
<ItemsControl x:Name="LayoutRoot" ItemsSource ="{Binding PopularHouses}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="5"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton
Content="{Binding FormattedPanelTimeRemaining}"
Style="{StaticResource MetroToggleButtonStyle}"
Height="45"
Width="80"
VerticalAlignment="Center"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
As you can see the ItemSource property is bound to the PopularHouses property on the ViewModel of the parent. This works great. However, what I want to do is set the ItemSource of the LayoutRoot element to a different property at the point on the parent form where the control is inserted in the XAML.
The end result should then be multiple instances of this user control, bound to several different properties on the parent's datacontext.
Could someone explain how to implement this?
You just have to bind your UserControl's DataContext to the datacontext of the first ContentControl using RelativeSource.
DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type ContentControl}}}"
I have made the following sample:
The mainwindow XAML
<Window x:Class="WpfDataContext.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfDataContext"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<local:UserControl1/>
</Grid>
</Window>
We set its datacontext to Self just for the purpose of this sample. In codebehind we define a simple property to show how it works:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string SomeString { get; set; } = "Hello";
}
Then, the usercontrol XAML:
<UserControl x:Class="WpfDataContext.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type ContentControl}}}">
<Grid>
<TextBlock Text="{Binding SomeString}"/>
</Grid>
</UserControl>
Note how we bind its DataContext property since this is the key.
I use a Textblock for simplicity, but the principle applies for your case also
I have a MainWindow.Xaml file. And one usercontrol PatientWindow.Xaml. How to load the patient window in mainwindow in MVVM architecture?
MainWindow.Xaml
<Window x:Class="PatientAdminTool.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:PatientAdminTool.ViewModel"
xmlns:v="clr-namespace:PatientAdminTool.View"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
ResizeMode="CanResizeWithGrip"
WindowStyle="None"
WindowState="Normal"
Title="PatientAdmin Tools"
Height="750"
Width="1400"
AllowsTransparency="True" MouseLeftButtonDown="OnMouseLeftButtonDown" BorderThickness="1" BorderBrush="#555252" >
<WindowChrome.WindowChrome>
<WindowChrome
CaptionHeight="0"
/>
</WindowChrome.WindowChrome>
</Window>
PatientWindow.xaml
Usercontrol window is mentioned in below
<UserControl x:Class="PatientAdminTool.View.PatientWindow"
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:vm="clr-namespace:PatientAdminTool.ViewModel"
xmlns:v="clr-namespace:PatientAdminTool.View"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" LastChildFill="True" Height="40" Background="#646161" >
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
<TextBlock Margin=" 10,5,0,0" HorizontalAlignment="Center" Text="Patients" FontSize="16" TextAlignment="Center" VerticalAlignment="Center" Foreground="#FFFFFF"/>
</StackPanel>
<StackPanel Margin="10,10,0,0" DockPanel.Dock="Right" Background="Transparent" Orientation="Vertical" HorizontalAlignment="Right" VerticalAlignment="Top" Width="50" >
<StackPanel Background="Transparent" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Focusable="False" ToolTip="Close" VerticalAlignment="Center" Background="#646161" BorderThickness="0" BorderBrush="Transparent" Padding="-4" Click="Button_Click" >
<Button.Content>
<Grid Width="45" Height="23">
<TextBlock Foreground="White" Text="r" FontFamily="Marlett" FontSize="20" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</Button.Content>
<Button.Template>
<ControlTemplate TargetType="Button">
<ContentPresenter Content="{TemplateBinding Content}"/>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</StackPanel>
</DockPanel>
</Grid>
</UserControl>
So I need to load the patient window above of main window using MVVM. Here I need to write load event in corresponding View Model. Please help me to do this.
Just add a ControlPresenter at MainWindow.
<ContentPresenter Content="{Binding YouTypeHere}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type fristViewModel1Type}">
<youControlForViewModel1 />
</DataTemplate>
<DataTemplate DataType="{x:Type secondViewModel2Type}">
<youControlForViewModel2 />
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
And you could change Views via binding different VM to ContentPresenter.
So you want to open a new child window using MVVM? I assume you would open it from MainWindowViewModel.
Solution 1: without strict MVVM
Sometimes its fine to open it directly from the ViewModel:
private void OnOpenPatientWindowCommandExecute()
{
var o = new PatientWindow();
o.ShowDialog();
}
For this you would have to change PatientWindow from UserControl to a Window.
Solution 2: strict MVVM
The solutions following strict MVVM are a little more complex. In the solution I write here you would have to use a Service, add it to your MainWindowViewModel and bind a control from the view to a Command in the ViewModel. Also, its written as if you are using Dependency Injection, that's why you see the service injected in the constructor. You can avoid this by just instantiating the service in the constructor.
MainWindowViewModel.cs
using Prism.Wpf.Commands; // For easy commands
using PatientAdminTool.Services; // Where you put your new service
public class MainWindowViewModel
{
private IShowDialogService _ShowDialogService;
public MainWindowViewModel(IShowDialogService showDialogService)
{
_ShowDialogService = showDialogService;
// Or do: _ShowDialogService = new ShowDialogService();
// But that's not a good practice and won't let you test
// this ViewModel properly.
OpenPatientWindowCommand = new DelegateCommand(OnOpenPatientWindowCommandExecute);
}
public ICommand OpenPatientWindowCommand { get; private set; }
private void OnOpenPatientWindowCommandExecute()
{
_ShowDialogService.ShowPatientWindow();
}
}
Services\IShowDialogService.cs
public interface IShowDialogService
{
void ShowPatientWindow();
void ShowOtherWindow();
// ...
}
Services\ShowDialogService.cs
public class ShowDialogService : IShowDialogService
{
public void ShowPatientWindow()
{
var patientWindowViewModel = new PatientWindowViewModel();
var patientWindow = new PatientWindow();
patientWindow.DataContext = patientWindowViewModel;
patientWindow.ShowDialog();
}
public void ShowOtherWindow()
{
// Other window ...
}
}
Finally, you make the connection in the View like this:
MainWindow.xaml
<Window
xmlns:vm="clr-namespace:PatientAdminTool.ViewModel">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Button Command="{Binding OpenPatientCommand}">Open Patient</Button>
</Grid>
</Window>
Haven't tried it in Visual Studio yet but that's the idea.
If it's the only thing you are going to be displaying in the same window, just put your view in there like any other control:
<Window x:Class="PatientAdminTool.MainWindow"
xmlns:v="clr-namespace:PatientAdminTool.View"... >
<WindowChrome.WindowChrome>
<WindowChrome
CaptionHeight="0"/>
</WindowChrome.WindowChrome>
<v:PatientWindow/>
</Window>
Do you have a root ViewModel for your window? If so, you can bind to a PatientViewModel:
<v:PatientWindow DataContext="{Binding PatientViewModel}"/>
If not, it's common to set the first DataContext to a ViewModel in the code-behind like this:
<v:PatientWindow Name="PatientWindowView"/>
and:
public partial class Window
{
public MainWindow()
{
InitializeComponent();
PatientWindowView.DataContext = new PatientWindowViewModel();
}
}
If you want to display more than one View, use a ContentPresenter like Shakra has answered.
If you want to open a new window, use what Alberto Cardona López has suggested.
I just started working with WPF and I'm trying to wrap my head around the XAML Bindings.
After a lot of reading I thought I had an easy enough usecase to test my knowledge. But nada :-( I can't figure it out.
What I'm trying to do is the following:
I have Address-Objects:
public IEnumerable<Address> Addresses
{
get
{
if (addresses == null)
{
addresses = new Repository<Address>().GetAll().ToList<Address>();
}
return addresses;
}
}
which have Rental-Objects e.g.
address.Rentals
also as IList.
On my MainWidow I want to have 2 UserControls, one for the address and one for the rental information. If I click the Navigator on the address user control, I only want to see the rental records of the current address.
MainWindow
<Window x:Class="Swopt.Mira.WPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:local="clr-namespace:Swopt.Mira.WPFApp"
Title="MainWindow" Height="500" Width="525">
<Window.Resources>
<sampleData:DataContext x:Key="testDataContext" />
</Window.Resources>
<Grid x:Name="rootGrid" Background="White" DataContext="{StaticResource testDataContext}">
<StackPanel>
<local:RentalCustomer x:Name="rentalCustomer" Height="100" />
<local:Rentals Height="100" />
</StackPanel>
</Grid>
Address
<UserControl
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:telerik="http://schemas.telerik.com/2008/xaml/presentation" x:Class="Swopt.Mira.WPFApp.RentalCustomer"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="RentalCustomerUserControl">
<Grid x:Name="rootGrid" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<StackPanel>
<TextBlock Text="Firma1"/>
<TextBox x:Name="Firma1" Text="{Binding ElementName=collectionNavigator, Path=CurrentItem.Firma1}" />
</StackPanel>
<telerik:RadCollectionNavigator Grid.Row="1"
Height="30"
Margin="2"
Grid.ColumnSpan="2"
x:Name="collectionNavigator"
Source="{Binding Addresses}"
/>
</Grid>
Rentals
<UserControl
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:telerik="http://schemas.telerik.com/2008/xaml/presentation" x:Class="Swopt.Mira.WPFApp.Rentals"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="RentalsUserControl">
<Grid x:Name="rootGrid" Background="White" >
<StackPanel>
<TextBlock Text="VertragsNr"/>
<TextBox x:Name="VertragsNr" Text="{Binding ElementName=collectionNavigator2, Path=CurrentItem.VertragsNr}" />
</StackPanel>
<telerik:RadCollectionNavigator Grid.Row="1"
Height="30"
Margin="2"
x:Name="collectionNavigator2"
Source="{Binding Rentals}" />
</Grid>
Essentially what I want to do is Bind the DataContext of UserControl2 (Rentals) to the CurrentItem of collectionNavigator in UserControl1 (Addresses)
But I can't figure out how. I tried many things, with Binding Path and RelativeSource but none seem to work. the only way I could get it to work was to copy the content of UserControl1 into my MainWindow.xaml, but then I can't reuse it.
Any help is much appreciated.
UPDATE >>>>>
Working solution from Sheridan.
MainWindows
...
<local:RentalCustomer x:Name="rentalCustomer" Height="400" />
<local:Rentals Height="100" DataContext="{Binding CurrentAddress, ElementName=rentalCustomer}" />
RentalCustomer.xaml
...
<telerik:RadCollectionNavigator Grid.Row="1"
Height="30"
Margin="2"
Grid.ColumnSpan="2"
x:Name="collectionNavigator"
Source="{Binding Addresses}"
CurrentItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=CurrentAddress, Mode=OneWayToSource}"
/>
RentalCustomer.xaml.cs
public Address CurrentAddress
{
get { return (Address)GetValue(CurrentAddressProperty); }
set { SetValue(CurrentAddressProperty, value); }
}
// Using a DependencyProperty as the backing store for CurrentAddress. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CurrentAddressProperty =
DependencyProperty.Register("CurrentAddress", typeof(Address), typeof(RentalCustomer), new PropertyMetadata(null));
You can data bind the current item of a collection using the "/" Binding notation. Try something like this:
<UserControl Name="Control1" DataContext="{Binding SomeCollection}" />
<UserControl Name="Control2" DataContext="{Binding SomeCollection/}" />
Binding SomeCollection/ means bind to the current item of the SomeCollection collection. To make this work, you may need to set the Selector.IsSynchronizedWithCurrentItem Property to True on any container controls that you are selecting the current item in:
<ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" ... />
UPDATE >>>
Without an IsSynchronizedWithCurrentItem property, the "/" Binding notation won't work. Instead, you could expose the selected item from the collection as a DependencyProperty on the relevant UserControl. Then you could do something like this:
<UserControl Name="Control2" DataContext="{Binding SelectedItem, ElementName=Control1}" />
You could provide the Rentals from your viewModel by binding the CurrentItem to your viewModel and expose the Rentals like this:
<telerik:RadCollectionNavigator CurrentItem={Binding CurrentItem} />
public Address CurrentItem
{
get; set;
}
public IEnumerable<Rentals> Rentals
{
get { return CurrentItem.Rentals; }
}
I have a window with a tabcontrol inside of it, and each tabitem will parent a frame, which is bound to a page object. But I can't seem to be able to make the frame/page resize to fit the "main window". Here's the code I have:
<controls:MetroWindow x:Class="Mplayer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Title="Mplayer"
Initialized="Window_Initialized" Height="770" Width="1125">
<TabControl HorizontalAlignment="Left" Height="45" VerticalAlignment="Top" Width="1125" Margin="0,0,0,0">
<TabItem Header="Home" x:Name="HomeTab">
<TabItem.Content>
<Frame Source="HomePage.xaml" Margin="0,0,7,-697"/>
</TabItem.Content>
</TabItem>
</TabControl>
</controls:MetroWindow>
And here's the "HomePage.xaml"
<Page x:Class="Mplayer.HomePage"
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="770" Width="1125"
Title="HomePage" Foreground="Black">
<Grid Background="#373737">
<Grid Background="#585858" HorizontalAlignment="Left" Width="400"/>
</Grid>
</Page>
I hope you understand my question, and thanks in advance :)
Remove harcoded height and width from page definition as well as TabControl definiton:
Height="770" Width="1125"
You set the height and width for TabControl and HomePage explicitly. Remove those values both in Page and TabControl.
<TabControl>
<TabItem Header="Home" x:Name="HomeTab">
<Frame Source="HomePage.xaml"/>
</TabItem>
</TabControl>