Set controls DataContext while binding other properties to parent context - xaml

I have the following class structure setup.
public class Control1
{
public Control1()
{
Control2Model = new Control2();
}
public Control2 Control2Model {get; set;}
public bool IsControlTwoVisible => true;
}
In my xaml I have a UserControl called CustomUserControl that I want to pass Control2Model to. I also want to bind IsControlTwoVisible to my CustomUserControls Visibility property.
<UserControl x:Class="Control1">
<StackPanel>
<!--Other controls above this-->
<CustomUserControl
DataContext="{Binding Control2Model }"
Visibility="{Binding IsControlTwoVisible, Converter={StaticResource VisibilityConverter}" />
</StackPanel>
</UserControl>
But I get a binding error because CustomUserControl cannot find IsControlTwoVisible on Control2Model.
Is there any way I set CustomUserControls context while still having access to Control1s properties?
Note I want to avoid moving IsControlTwoVisible to Control2.

I would suggest to not set DataContext in the Control1 but instead to the top Grid inside CustomUserControl. By doing that it will solve your problem.
Please refer to below code for reference:
Control1.xaml
<UserControl x:Class="Control1">
<StackPanel>
<!--Other controls above this-->
<CustomUserControl
Visibility="{Binding IsControlTwoVisible, Converter={StaticResource VisibilityConverter}" />
</StackPanel>
</UserControl>
CustomUserControl.xaml
<UserControl
x:Class="UWPBlankApp.CustomUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWPBlankApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid DataContext="{Binding Control2Model}">
<!--Other controls-->
</Grid>
</UserControl>

Related

Multiple DataTemplates with UWP MapItemsControl

I have a MapControl in UWP:
<maps:MapControl x:Name="BikeMap" ZoomLevel="17" Center="{Binding CenterPoint, Mode=TwoWay}">
<maps:MapItemsControl x:Name="MapItems" ItemsSource="{Binding BikePoints}"
ItemTemplate="{StaticResource BikePointTemplate}"/>
</maps:MapControl>
and am adding MapElements using XAML data templates, my ItemsSource is a list of simple objects.
But, UWP doesn't seem to provide a way to specify the DataType of a DataTemplate and the MapItemsControl doesn't have a property for setting a DataTemplateSelector.
Does anyone know how I can use multiple data templates with the MapItemsControl and have the relevent data template selected based on the object type within the ItemsSource?
MapItemsControl Class does not have a property for setting DataTemplateSelector. To achieve what you want, we can take advantage of ContentControl by setting it as the template content in DataTemplate and then using ContentControl.ContentTemplateSelector property to set DataTemplateSelector.
Following is a simple sample:
XAML:
<Page x:Class="UWPApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Maps="using:Windows.UI.Xaml.Controls.Maps"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:UWPApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="GreenDataTemplate">
<StackPanel Background="Green">
<TextBlock Margin="5"
Maps:MapControl.Location="{Binding Location}"
Maps:MapControl.NormalizedAnchorPoint="0.5,0.5"
FontSize="20"
Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="RedDataTemplate">
<StackPanel Background="Red">
<TextBlock Margin="5"
Maps:MapControl.Location="{Binding Location}"
Maps:MapControl.NormalizedAnchorPoint="0.5,0.5"
FontSize="20"
Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<local:MyTemplateSelector x:Key="MyTemplateSelector" GreenTemplate="{StaticResource GreenDataTemplate}" RedTemplate="{StaticResource RedDataTemplate}" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Maps:MapControl x:Name="MyMap" MapServiceToken="MapServiceToken">
<Maps:MapItemsControl x:Name="MyMapItemsControl" ItemsSource="{Binding}">
<Maps:MapItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource MyTemplateSelector}" />
</DataTemplate>
</Maps:MapItemsControl.ItemTemplate>
</Maps:MapItemsControl>
</Maps:MapControl>
</Grid>
</Page>
Code-Behind:
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate GreenTemplate { get; set; }
public DataTemplate RedTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
if (item != null)
{
if (item is GreenPOI)
{
return GreenTemplate;
}
return RedTemplate;
}
return null;
}
}
public class POI
{
public string Name { get; set; }
public Geopoint Location { get; set; }
}
public class GreenPOI : POI { }
public class RedPOI : POI { }
This is just for example. In the sample, I used two data template with different background and I create a custom DataTemplateSelector which can choose DataTemplate based on the object type. And if you have several object types, you can also refer to this answer: How to associate view with viewmodel or multiple DataTemplates for ViewModel?

Binding ViewModel property to the primary View or sub view if primary view including sub view?

I have a primary View. It includes FirstSubView and SecondSubView.
<navigation:Page x:Class="Test.Views.PreimaryView"
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:localViews="clr-namespace:Test.Views"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="1057" d:DesignHeight="707"
Title="TestView Page">
<UserControl.Resources>
<silverlightLib:BooleanVisibilityConverter x:Key="BooleanVisibilityConverter"></silverlightLib:BooleanVisibilityConverter>
</UserControl.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="Transparent">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<localViews:FirstSubView DataContext="{Binding VmFirst, Mode=TwoWay}"></localViews:FirstSubView>
<localViews:SecondSubView DataContext="{Binding VmSecond, Mode=TwoWay}" Visibility="{Binding IsAdjustVisible, Converter={StaticResource BooleanVisibilityConverter}}"></localViews:SecondSubView>
</StackPanel>
</StackPanel>
</ScrollViewer>
You see in the SecondSubView I have a binding property IsAdjustVisible.
My question is where an I set it? I set up it in both ViewModels to adjust the visibility of the second View. But it doesn't work, even when it is false, the View is still shown.
Finally I found the issue. I only need one place to put the properties. Which is the sub-View rather than the Main View.

How to create a main layout template in XAML

I'm new to windows development. I'm making a simple windows app which has a few pages and each page has a similar layout in XAML. Like this:
Each page is separated into 3 sections. A will have a title, B is where the content will be inserted and C is for other stuff. My question is: what is the simplest way to build a general layout template so that I can reuse it for every page? And is it possible?
For example, I have a MainTemplate.xaml file with this layout:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
</Grid>
And then my Page1.xaml will load MainTemplate so I don't have to copy and paste the same layout into every page of mine. I've tried looking online but the solutions are going way over my head. I was wondering if there's a simple way to do this like with webpages.Thanks a lot.
One feasible way to do this is using UserControl with ContentPresenter. For example:
Add a UserControl named MainTemplate. In the XAML, set the layout with ContentPresenter and bind it to the DependencyProperty defined in code-behind.
<UserControl x:Class="UWPTest.MainTemplate"
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:local="using:UWPTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ContentPresenter Content="{x:Bind Title}" />
<ContentPresenter Grid.Row="1" Content="{x:Bind Main}" />
<ContentPresenter Grid.Row="2" Content="{x:Bind Stuff}" />
</Grid>
</UserControl>
In the code-behind, set the DependencyProperty so that we can use them to set the content in other pages.
public sealed partial class MainTemplate : UserControl
{
public MainTemplate()
{
this.InitializeComponent();
}
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(object), typeof(MainTemplate), new PropertyMetadata(null));
public object Title
{
get { return GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty MainProperty = DependencyProperty.Register("Main", typeof(object), typeof(MainTemplate), new PropertyMetadata(null));
public object Main
{
get { return GetValue(MainProperty); }
set { SetValue(MainProperty, value); }
}
public static readonly DependencyProperty StuffProperty = DependencyProperty.Register("Stuff", typeof(object), typeof(MainTemplate), new PropertyMetadata(null));
public object Stuff
{
get { return GetValue(StuffProperty); }
set { SetValue(StuffProperty, value); }
}
}
After this, we can use the UserControl in other pages to reuse the general layout. For example, using it in "MainPage.xaml":
<Page x:Class="UWPTest.MainPage"
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:local="using:UWPTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<local:MainTemplate>
<local:MainTemplate.Title>
<Grid Background="Red">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="60">A</TextBlock>
</Grid>
</local:MainTemplate.Title>
<local:MainTemplate.Main>
<Grid Background="Green">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="60">B</TextBlock>
</Grid>
</local:MainTemplate.Main>
<local:MainTemplate.Stuff>
<Grid Background="Yellow">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="60">C</TextBlock>
</Grid>
</local:MainTemplate.Stuff>
</local:MainTemplate>
</Page>
Then the "MainPage" will look like follwoing:

XamlParseException when trying to bind subclass Loaded event handler

I am having these same problems in my Windows Phone Project:
XamlParseException when adding event handler in XAML
http://social.msdn.microsoft.com/Forums/vstudio/en-US/7624b2fc-0dea-415f-a71d-1739020d9d2e/xamlparseexception-when-trying-to-bind-subclass-loaded-event-handler?forum=wpf
However, it seems like my handler does have the correct signature?
Even stranger, I did the same thing with the UIElement_Tap event, in another page and it works fine.
MainPage.xaml
<v:PageBase
x:Class="PhoneApp1.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:v="clr-namespace:PhoneApp1.Views"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0"
Loaded="ItemsControl_Loaded"
Margin="0,0,0,120">
<TextBlock Text="Item1"/>
<TextBlock Text="Item2"/>
<TextBlock Text="Item3"/>
</ItemsControl>
<ItemsControl Grid.Row="1"
Loaded="ItemsControl_Loaded"
Margin="0,120,0,0">
<TextBlock Text="Item1"/>
<TextBlock Text="Item2"/>
<TextBlock Text="Item3"/>
<TextBlock Text="Item4"/>
</ItemsControl>
</Grid>
</v:PageBase>
MainPage.xaml.cs
namespace PhoneApp1.Views
{
public partial class MainPage : PageBase
{
// Constructor
public MainPage()
{
InitializeComponent();
}
}
}
PageBase.cs
using Microsoft.Phone.Controls;
namespace PhoneApp1.Views
{
// Common code across pages.
public class PageBase : PhoneApplicationPage
{
protected virtual void ItemsControl_Loaded(object sender, System.Windows.RoutedEventArgs e )
{
//...
}
}
}
I know I could override the method and call the base type in my MainPage, but that defeats the purpose of having a common base, no?
This is how I ended up fixing this:
Gave the itemscontrol a name and wired up the event in the MainPage constructor:
itemControl1.Loaded += base.ItemsControl_Loaded;
Try to replace System.Windows.RoutedEventArgs with EventArgs

Binding the value of an UIElement within GridViewDataColumn.Header not working

I am trying to show a circle in a telrik gridview and I wanted to bind the colour dynamically to this circle.Since I am working with MVVM pattern I have to bound my view model to the datacontext of my page.But the binding does not seems to be working for me.When I investigated the issue was because the column headers does not have any datacontext, so I tried bound the value using 'ElementName' and tried to use its datacontext but even that is also not working.
Could anyone please help me resolving this issue.
This is my xaml code
<UserControl x:Class="TelrikStyling.MainPage"
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:telerikGrid="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:myColour="clr-namespace:TelrikStyling"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" x:Name="myMainPage">
<Grid x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded">
<telerikGrid:RadGridView x:Name="radGridView"
AutoGenerateColumns="False">
<telerikGrid:RadGridView.Columns>
<telerikGrid:GridViewDataColumn>
<telerikGrid:GridViewDataColumn.Header>
<StackPanel Orientation="Horizontal">
<Ellipse x:Name="headerEllipse" Width="20" Height="20" Fill="{Binding ElementName=myMainPage, Path=DataContext.Colour}"></Ellipse>
</StackPanel>
</telerikGrid:GridViewDataColumn.Header>
</telerikGrid:GridViewDataColumn>
</telerikGrid:RadGridView.Columns>
</telerikGrid:RadGridView>
</Grid>
</UserControl>
This is my view model
public MainPage()
{
InitializeComponent();
this.DataContext = new MainPageViewModel { Colour = new SolidColorBrush(Colors.Red) };
}
Try using RelativeSource instead of ElementName in your Binding
I've had similar issues in the past where I couldn't resolve a binding using ElementName but could with RelativeSource. I think it may have to do with the timing of resolving the binding.
<Ellipse Fill="{Binding RelativeSource={RelativeSource
AncestorType={x:Type myColour:MainPage}},
Path=DataContext.Colour}" />
I have found a solution for my problem that is by adding new ellipse to the layout as a child of the layout grid and then bind the Colour property of the viewmodel to the Fill property of new ellipse.Later I have bound Fill property of the ellipse in the RadGrid to the newly added ellipse.
This is the new xaml
<UserControl x:Class="TelrikStyling.MainPage"
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:telerikGrid="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:myColour="clr-namespace:TelrikStyling"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" x:Name="myMainPage">
<Grid x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded">
<telerikGrid:RadGridView x:Name="radGridView"
AutoGenerateColumns="False">
<telerikGrid:RadGridView.Columns>
<telerikGrid:GridViewDataColumn>
<telerikGrid:GridViewDataColumn.Header>
<StackPanel Orientation="Horizontal">
<Ellipse x:Name="headerEllipse" Width="20" Height="20" Fill="{Binding ElementName=invisibleEllipse, Path=Fill}"></Ellipse>
</StackPanel>
</telerikGrid:GridViewDataColumn.Header>
</telerikGrid:GridViewDataColumn>
</telerikGrid:RadGridView.Columns>
</telerikGrid:RadGridView>
<Ellipse x:Name="invisibleEllipse" Width="20" Height="20" Fill="{Binding Colour}"></Ellipse>
</Grid>
</UserControl>
Can anyone tell me why the binding was not working when I gives myMainPage as the element name and it started working when I given a new elisple?
Untill someone answer to this question, I am marking this as an answer.