DataTemplate doesn't properly bind the properties as specified - c#

I have the following DataTemplate in DataTemplates.xaml
<DataTemplate DataType="{x:Type local:ExcelReportVM}">
<local:ExcelReport DoubleClickHandler="{Binding}">
<local:ExcelReport.RowColorConverter>
<local:ReportRowColorConverter/>
</local:ExcelReport.RowColorConverter>
</local:ExcelReport>
</DataTemplate>
I make sure that this DataTemplate is usable within the application scope via the following App.xaml definition:
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DataTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
And this is my control ExcelReport.xaml:
<UserControl x:Class="WpfApplication1.ExcelReport"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
....
xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<DataTemplate x:Key="HeaderTemplate">
<TextBlock TextAlignment="Center" Text="{Binding .}" TextWrapping="WrapWithOverflow" />
</DataTemplate>
</UserControl.Resources>
<UserControl.DataContext>
<local:ExcelReportVM/>
</UserControl.DataContext>
<syncfusion:SfDataGrid ItemsSource="{Binding Entries}" x:Name="grid" Background="White"
HeaderTemplate="{StaticResource HeaderTemplate}"
SelectedItem="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=UserControl}}">
</syncfusion:SfDataGrid>
</UserControl>
My code behind: ExcelReport.xaml.cs
public partial class ExcelReport : UserControl
{
public static readonly DependencyProperty RowColorConverterProperty = DependencyProperty.Register(
"RowColorConverter",
typeof(IValueConverter),
typeof(ExcelReport),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnRowColorConverterChanged))
);
public IValueConverter RowColorConverter
{
get { return (IValueConverter)GetValue(RowColorConverterProperty); }
set { SetValue(RowColorConverterProperty, value); }
}
public ExcelReport()
{
InitializeComponent();
this.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Debug.Assert(DataContext.GetType()==typeof(ExcelReportVM)); //DataContext is correct
Debug.Assert(RowColorConverter!=null); //but this is null
}
}
I've no idea why I can successfully bind the DataContext, but the <DataTemplate DataType="{x:Type local:ExcelReportVM}"> that I define is not used, I thought everything inside the DataTemplates.xaml is accessible to ExcelReport.xaml?
Note: There is no error in the output window, and there is no exception been thrown anywhere in the code.

If it is a ContentControl, then you have to set its Content as <ContentControl Content="{Binding SomeProperty}"/> , then based on DataType of SomeProperty correct DataTemplate is picked up.

This is how I fix the problem, I don't set the UserControl.DataContext to ExcelReportVM at ExcelReport.xaml; instead, I set the ContentControl.Content to ExcelReportVM at MainWindow.xaml.
Updated code at ExcelReport.xaml:
<UserControl x:Class="WpfApplication1.ExcelReport"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
....
xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<DataTemplate x:Key="HeaderTemplate">
<TextBlock TextAlignment="Center" Text="{Binding .}" TextWrapping="WrapWithOverflow" />
</DataTemplate>
</UserControl.Resources>
<!-- <UserControl.DataContext> this is not longer needed
<local:ExcelReportVM/>
</UserControl.DataContext> -->
<syncfusion:SfDataGrid ItemsSource="{Binding Entries}" x:Name="grid" Background="White"
HeaderTemplate="{StaticResource HeaderTemplate}"
SelectedItem="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=UserControl}}">
</syncfusion:SfDataGrid>
</UserControl>
Code at MainWindow.xaml
<Syncfusion:RibbonWindow x:Class="WpfApplication1.MainWindow"
...
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" WindowState="Maximized"
Syncfusion:SkinStorage.VisualStyle="Office2013"
xmlns:Syncfusion="http://schemas.syncfusion.com/wpf">
<Grid x:Name="ExcelReport22">
<ContentControl> <!-- this is where the ExcelReportVM is binded to -->
<local:ExcelReportVM/>
</ContentControl>
</Grid>
</Syncfusion:RibbonWindow>

Related

Load resources located in seperate files inside the MainWindow.xaml

Goal: Creating an XAML template which I can reuse and load into my main view. Is this possible? If so how? Ive read about the ResourceDictionary and came up with something but im not sure where to continue from there.
This is the XAML with my resource (kept very dumb and simple):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel x:Key="myUnneccesaryButtonList">
<Button Content="1"></Button>
<Button Content="2"></Button>
<Button Content="3"></Button>
</StackPanel>
</ResourceDictionary>
Here my MainWindow XAML where I want to use the above template and load it:
<Window x:Class="Sample.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"
mc:Ignorable="d"
Title="Sample" WindowState="Maximized">
<StackPanel x:Name="wrapper" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</StackPanel>
</Window>
Edit: Here is my MainWindow but the Window.Resource declaration doesnt work:
<Window x:Class="Sample.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"
mc:Ignorable="d"
Title="Sample" WindowState="Maximized">
<Window.Resources>
<ResourceDictionary Source="MyDictionary.xaml" >
</Window.Resources>
<StackPanel x:Name="wrapper" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</StackPanel>
</Window>
myUnneccesaryButtonList is not a template but an actual StackPanel instance.
If you set its x:Shared attribute to false in the ResourceDictionary:
<StackPanel x:Key="myUnneccesaryButtonList" x:Shared="False">
<Button Content="1"></Button>
<Button Content="2"></Button>
<Button Content="3"></Button>
</StackPanel>
..you could use a ContentControl to display it in the window:
<ContentControl Content="{StaticResource myUnneccesaryButtonList}" />
What you probably want to do is to create a custom StackPanel class that always adds the Buttons though:
public class CustomStackPanel : System.Windows.Controls.StackPanel
{
public CustomStackPanel()
{
Children.Add(new Button() { Content = "1" });
Children.Add(new Button() { Content = "2" });
Children.Add(new Button() { Content = "3" });
}
}
Usage:
<local:CustomStackPanel x:Name="wrapper" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
if you want a XML Template then you should create a template
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="myUnneccesaryButtonList">
<StackPanel >
<Button Content="1"></Button>
<Button Content="2"></Button>
<Button Content="3"></Button>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
then you could define a control to host it
<ContentControl ContentTemplate="{StaticResource myUnneccesaryButtonList}" />
or
<ItemsControl ItemTemplate="{StaticResource myUnneccesaryButtonList}" />
Remember add the dictionary into the Resources
<Window.Resources>
<ResourceDictionary Source="YourDictionary.xaml" />
</Window.Resources>
or to merge it in
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="YourDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>

How to avoid repeated xaml code

I have a small UI in XAML where I need to display twice the same thing in the same window. I created a Resources with the code, but can't figure out how to display it.
the resources :
<max:MaxUserControl.Resources>
<DataTemplate x:Key="tInfo">
<max:MaxGrid>
<max:MaxGrid.ColumnDefinitions>
...
</max:MaxGrid.ColumnDefinitions>
<max:MaxGrid.RowDefinitions>
...
</max:MaxGrid.RowDefinitions>
...
</max:MaxGrid>
</DataTemplate>
</max:MaxUserControl.Resources>
The only difference between both UI is the Datacontext, so I wanted to do something like :
<max:MaxStackPanel Grid.Row="1" Grid.Column="0" Template="{StaticResource ResourceKey=tInfo}" DataContext="{Binding ElementName=dtgEmployeeOccupation, Path=SelectedItem, Mode=OneWay}"/>
<max:MaxStackPanel Grid.Row="0" Grid.Column="1" Template="{StaticResource ResourceKey=tInfo}" DataContext="{Binding Path=ANOTHERBINDING"/>
What control I should use to achieve this?
Create UserControl and reuse where you want. At first you should create
UserControl, then add some necessary controls inside your UserControl. For example, we are creating UserControl and it would be called FooUserControl:
<UserControl x:Class="OpenExcelFileAndConvertToArray.FooUserControl"
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:OpenExcelFileAndConvertToArray"
mc:Ignorable="d">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="SomeText"/>
<Button Content="Delete"/>
</StackPanel>
</Grid>
</UserControl>
Then just in any other controls you can reuse this FooUserControl. For example:
<Window x:Class="OpenExcelFileAndConvertToArray.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:OpenExcelFileAndConvertToArray"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<ComboBox Text="qq" Name="comboBox">
<ComboBoxItem Content="1"/>
<ComboBoxItem Content="2"/>
<ComboBoxItem Content="3"/>
</ComboBox>
<!--reusable control-->
<local:FooUserControl/>
</StackPanel>
</Grid>

Creating a Custom ScrollViewer using WPF UserControl

I'm trying to create a ScrollViewer with some customizations. Like this:
UserControl1.xaml:
<UserControl x:Class="MyApp.Control.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:DesignHeight="100" d:DesignWidth="300">
<UserControl.Template>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Content="{Binding ElementName=uc, Path=DynamicUserControl}" />
<Rectangle Grid.Row="1" Fill="#88ff0000" />
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
UserControl1.xaml.cs:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static readonly DependencyProperty DynamicUserControlProperty = DependencyProperty.Register("DynamicUserControl", typeof(object), typeof(UserControl1), new PropertyMetadata(null));
public object DynamicUserControl
{
get { return GetValue(DynamicUserControlProperty); }
set { SetValue(DynamicUserControlProperty, value); }
}
}
TestForm.xaml (Using the UserControl1):
<Window x:Class="MyApp.TestForm"
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:MyApp.Control"
mc:Ignorable="d"
Title="TestForm" Height="200" Width="500">
<Grid Background="{StaticResource AimDarkGradBg01}">
<local:UserControl1>
<local:UserControl1.DynamicUserControl>
<Button>Click me</Button>
</local:UserControl1.DynamicUserControl>
</local:UserControl1>
</Grid>
</Window>
But the problem is no matter what content I put in the local:UserControl1.DynamicUserControl, nothing is rendered.
Anyone can help me?
The problem is actually you binding expression. The correct binding should be like:
UserControl1.xaml:
<UserControl x:Class="MyControls.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"
xmlns:local="clr-namespace:MyControls"
mc:Ignorable="d"
x:Name="uc">
<Grid>
<ScrollViewer>
<WrapPanel>
<!-- Dynamic Content -->
<ContentPresenter Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type control:UserControl1}}, Path=DynamicUserControl}"/>
</WrapPanel>
</ScrollViewer>
<Canvas>
<!-- Some Code -->
</Canvas>
</Grid>
</UserControl>
If you noticed I removed your definition of the template, in which case you don't need it. You can simply just put your code inside the user control.
The other files are correct. Fix what I told you above and you're good to go.

WPF Navigate from frame in window to Usercontrol and pass parameters to it

I have a modern window in WPF/C# application, in which I added a modern frame:
<mui:ModernWindow x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
WindowStartupLocation="CenterScreen"
Style="{StaticResource EmptyWindow}">
<Window.Resources>
</Window.Resources>
<Grid>
<Menu x:Name="menu" Height="62" VerticalAlignment="Top" >
<MenuItem x:Name="miHome" Header="Home" Click="MenuItem_Home" IsChecked="True" Width="60" FontSize="14" />
<MenuItem x:Name="miClients" Header="Clients" FontSize="14" Click="MenuItem_Clients" Width="65"/>
<MenuItem x:Name="miSuppliers" Header="Suppliers" FontSize="14" Click="MenuItem_Suppliers" Width="81"/>
<MenuItem x:Name="miReports" Header="Reports" FontSize="14" Click="MenuItem_Reporting" Width="71"/>
</Menu>
<mui:ModernFrame Margin="0,75,10,10" x:Name="frame">
</mui:ModernFrame>
</Grid>
I have MenuItems in my application, when I click on Suppliers item, I fill the frame with a usercontrol, like this:
frame.Source = new Uri("/Pages/Suppliers.xaml", UriKind.Relative);
Where Suppliers.xaml design is:
<UserControl
x:Class="MyApp.LinksBar.Suppliers"
xmlns:MyApp="clr-namespace:MyApp"
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:mui="http://firstfloorsoftware.com/ModernUI"
mc:Ignorable="d"
d:DesignHeight="575" d:DesignWidth="905">
<UserControl.Resources>
</UserControl.Resources>
<Grid Name="Grid">
<mui:ModernButton x:Name="btnmakePayment" Content="Make Payment" Click="btnMakePayment_Click" Grid.Row="2" HorizontalAlignment="Right" VerticalAlignment="Top"/>
</Grid>
</UserControl>
When I click on "Make Payment" button, I navigate to another UserControl (MakePayment.xaml):
private void btnMakePayment_Click(object sender, RoutedEventArgs e)
{
NavigationCommands.GoToPage.Execute(new Uri("/Actions/MakePayment.xaml", UriKind.Relative), this);
}
MakePayment.xaml design is:
<UserControl
xmlns:local="clr-namespace:MyApp.Actions" x:Class="MyApp.Actions.MakePayment"
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:mui="http://firstfloorsoftware.com/ModernUI" xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:MyApp="clr-namespace:MyApp"
mc:Ignorable="d"
Loaded="MakePayment_Loaded"
d:DesignHeight="600" d:DesignWidth="866" >
<UserControl.Resources>
</UserControl.Resources>
<Grid DataContext="{StaticResource makePaymentViewSource}" Name="Grid">
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<Label Content="Total" VerticalContentAlignment="Center" HorizontalAlignment="Left" VerticalAlignment="Top"/>
// More design code here ...
</Grid>
</UserControl>
Here comes my question:
I need to pass parameters from Suppliers UserControl to MakePayment UserControl.
How to programmatically pass the parameters in Suppliers and read them in MakePayment?
Thank you.
If you can bind one to the other, that is what you should be doing. By far, the easiest way to make two UserControls "communicate" (or share properties that both can do stuff with) is to define a DependencyProperty on each and bind them two-way.
This way, both always have access to the same value and both can do stuff with it.
Take my own control as an example:
<UserControl x:Class="MyControls.MasterContainerControl"
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:MyControls"
mc:Ignorable="d"
x:Name="masterContainerControl">
<local:ContainerControl SomeProperty="{Binding ElementName=masterContainerControl, Path=SomeProperty}">
<local:ContainerControl.Another>
<local:AnotherControl SomeProperty="{Binding ElementName=masterContainerControl, Path=SomeProperty"/>
</local:ExplorerBase.AddressBar>
<local:ContainerControl.Some>
<local:SomeControl SomeProperty="{Binding ElementName=masterContainerControl, Path=SomeProperty"/>
</local:ContainerControl.Some>
</local:ContainerControl>
</UserControl>
This, of course, all assumes MasterContainerControl, ContainerControl, AnotherControl, and SomeControl all have a DependencyProperty called SomeProperty, and then the bindings seal the deal.
Note: Make sure the default values are defined in MasterContainerControl because those will override the values MasterContainerControl binds to.
If I misunderstood your issue, please let me know.

Master Detail MVVM with Prism and XAML binding view in data context

I have the following dummy application where I'm trying to build a Master Detail with two views. The first is the collection view, which I can successfully select an element of and it displays in the Content Presenter Data Template with the TextBlock and TextBox defined as they are below.
I have tried to Move the TextBlock and TextBox out to a view, but have been unsuccessful at getting it to display the data. If I remove the TBs and uncomment the view, it will display the view but the TBs in the view won't populate.
Of course, the idea is that I will have more than one type.
MainWindow
<Window x:Class="MyApp.Views.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:views="clr-namespace:MyApp.Views"
mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/"
xmlns:viewModel="clr-namespace:MyApp.ViewModels"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Horizontal">
<views:CollectionView DataContext="{Binding myItemCollection}">
</views:CollectionView>
<ContentPresenter x:Name="Detail" Content="{Binding myItemCollection.SelectedViewModel}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type viewModel:TextViewModel}">
<StackPanel>
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBox Text="{Binding Text}"></TextBox>
<!--<views:TextView/>-->
</StackPanel>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</StackPanel>
</Grid>
TextView
<UserControl x:Class="MyApp.Views.TextView"
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:MyApp.Views"
xmlns:viewModel="clr-namespace:MyApp.ViewModels"
mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel>
<TextBlock Text="Text Item"/>
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBox Text="{Binding Text}"></TextBox>
</StackPanel>
</Grid>
You need to remove the prism:ViewModelLocator.AutoWireViewModel="True" attribute from your TextView.
What it does is it pulls the appropriate view model from the container and assigns the view model to TextView.DataContext. On the other hand in your template you do not explicitly pass the templated data to the TextView control, so it's expected to be inherited via automatic inheritance of DataContext. But that does not work, because TextView.DataContext is explicitly set by prism:ViewModelLocator.AutoWireViewModel="True".
In case it is required to use view model auto wiring you can always set this attribute from referencing scope, that is in XAML in which your view is "used", e.g.:
<StackPanel>
<views:TextView prism:ViewModelLocator.AutoWireViewModel="True" />
</StackPanel>

Categories