Dynamically change a static resource - c#

In my App.xaml I have two resources
<x:Double x:Key="MasterGridSize">150</x:Double>
<DataTemplate x:Key="MasterGridItemTemplate">
<Grid Width="{StaticResource MasterGridSize}" Height="{StaticResource MasterGridSize}">
</Grid>
</DataTemplate>
When I change the value of MasterGridSize it does not change the grid size - as expected. How can I achieve this?
I tried ThemeResource, then it's at least changing, when the theme changes.
DynamicResource is not available.
I don't want to add MasterGridSize to the ViewModel, since this would lead to some dependencies between ViewModels and some update code.
Any other ideas?
Edit: It might also be reasonable to edit the DataTemplate directly - how would I achieve this?

You need to use a value holder, like the BindableValueHolder from the UWP Community Toolkit.
<helpers:BindableValueHolder x:Key="MasterGridSize">
<helpers:BindableValueHolder.Value>
<x:Double>150</x:Double>
</helpers:BindableValueHolder.Value>
</helpers:BindableValueHolder>
<DataTemplate x:Key="MasterGridItemTemplate">
<Grid Width="{Binding Value, Source={StaticResource MasterGridSize}}" Height="{Binding Value, Source={StaticResource MasterGridSize}}" />
</DataTemplate>
Source code available here!

Have you declared your resources very well? If yes, try GridLength as follows:
<Page.Resources>
<GridLength x:Key="FirstSize">5*</GridLength>
<GridLength x:Key="SecondSize">4*</GridLength>
</Page.Resources>
<DataTemplate x:Key="MasterGridItemTemplate">
<Grid Width="{StaticResource FirstSize}" Height="{StaticResource SecondSize}">
</Grid>
</DataTemplate>

Related

Bind ListView in ControlTemplate

I have a ControlTemplate in my App.xaml like bellow:
<ControlTemplate x:Key="MyTempplate">
<ListView x:Name="MyListView"
AutomationId="SelectRelationListViewId"
ItemsSource="{Binding MyListViewData}"
SelectedItem="{Binding MySelectedItem}"
BackgroundColor="White"
RowHeight="60"
SeparatorColor="White"
IsGroupingEnabled="False"
VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor="#E7E4E0"
Padding="20,0,20,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Text="{Binding MyName}"
VerticalOptions="CenterAndExpand"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ControlTemplate>
My page is bound with my ViewModel and if I copy/paste the code from my template to my pagem, data and events are correctly handled but nothing happend when I try to use my template, the ListView is empty and the events are not triggered.
I already tried "{TemplateBinding DataContext.MyPropertyNameToBind}".
We need more details on your code and your intention on creating a control template but here are the steps that can help you:
1- First: ControlTemplate utility,
in Xamarin Forms, a 'ControlTemplate' is used to customize appearance of a control. More precisely it defines the layout in which the content will be displayed.
So it should contain a 'ContentPresenter' object.
It can be applied only on 'ContentPage' and 'ContentView' objects
--> On which UI 'Element' are you trying to bind your ControlTemplate to ?
2- Then using TemplateBindings (I suspect a problem here)
If I'm right, TemplateBindings can only be used to bind to 'Parent' Bindable Properties.
When you are doing:
<ControlTemplate x:Key="MyTemplate">
<ListView ItemsSource={TemplateBinding BindingContext} />
...
It's ok because 'BindingContext' is a BindableProperty, but if you do:
<ControlTemplate x:Key="MyTemplate">
<ListView ItemsSource={TemplateBinding BindingContext.MyItems} />
...
It's ko because 'BindingContext.MyItems' is not a BindableProperty (I presume)
3- Finally, what to do ?
Solution 1
Create in your page that owns the templated control, intermediate BindableProperties that bind to your ViewModel.
Then your 'TemplateBindings' should reference these properties.
A good example using 'TemplateBinding' & 'BindableProperty' here:
Xamarin TemplateBinding documentation.
Solution 2
For me, I will prefer to extract to App.xaml,
the ListView DataTemplate to a new data template resource
the Listview properties into a new 'ListView' Style
And in my page I would have:
<Page ...>
<ListView x:Name="myList"
Style="{StaticResource MyListViewStyle}"
ItemsSource="{Binding vmItems}"
DataTemplate="{StaticResource MyListViewDataTemplate}"
/>
In fact you can include the 'DataTemplate' property inside your Style directly...
Let me know if it helps.

Navigation between page

I'm writing a small WPF app with only 3 pages (for now). I'm using DataTemplate and ContentControl in the main window to display and switch between my pages. (See code sample below). It's working but I have a few concerns:
The DataTemplate use parameterless constructor only. If I add one then It can't find the constructor.
The 'registration' is done in the xaml and I cannot use Dependency Injection to link Views with ViewModels.
Questions:
Is there a way to change that without using third party tool?
If the only good option is to use a tool, which ones should I consider?
<Window.Resources>
<DataTemplate DataType="{x:Type pageViewModels:HomePageViewModel}">
<pageViews:HomePageView />
</DataTemplate>
<DataTemplate DataType="{x:Type pageViewModels:GamePageViewModel}">
<pageViews:GamePageView />
</DataTemplate>
</Window.Resources>
<DockPanel>
<Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">
<ItemsControl ItemsSource="{Binding PageViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding }"
Margin="2,5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
Edit
To clarify, I want to inject a class in the constructor of my viewsModels, but if I do that then the navigation within my application is broken because the dataTemplate is looking for the parameterless constructor.
It looks like my problem has been very well explained in this post.
In short, I need to implement a ViewModelLocator pattern in order to fix all my concerns.

WPF UserControl not rendering. UserControl is instantiated using DataTemplates

I have a user control that I`m trying to show via data templates. Unfortunately, the control never shows. Here's the DataTemplate for it:
<DataTemplate x:Key="listViewTemplate">
<ctrls:SpecialControl/>
</DataTemplate>
If I put regular controls inside those DataTemplate tags, I can see them. My SpecialControl however, won't show. Here's the xaml file for my SpecialControl:
<UserControl x:Class="CustomControls.SpecialControl"
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">
<StackPanel>
<CheckBox Content="Hello World" />
<Button >
<TextBlock Text="Goodbye world"/>
</Button>
</StackPanel>
</UserControl>
For some reason, this control is invisible at run time. If I put them directly into the template, I'll see them. I know I could just do that, but I want to do something more complex with this class, using databinding, and custom behavior. I tried using Snoop, and I can see my SpecialControl in there somewhere, with a ContentPresenter that's not expandable: no sign of The checkbox or the button.
Edit:
Here is the View that is using the SpecialControl: I left out many of the templates I'm using, because I don't want this to be too crowded.
<UserControl x:Class="Tools.EditorWindow"
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:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
xmlns:ctrls="clr-namespace:CustomControls"
xmlns:local="clr-namespace:Tools"
mc:Ignorable="d"
x:Name="This">
<UserControl.Resources>
<!--More templates -->
<DataTemplate x:Key="groupBoxTemplate" >
<ctrls:SpecialGroupBox Header="{Binding Path=Title}" Margin="2" DockPanel.Dock="Top">
<ItemsControl ItemsSource="{Binding guiItemsList}" ItemTemplateSelector="{DynamicResource guiTemplateSelector}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel>
<!-- Set up the width of the individual items based on how many columns we are supposed to have and what the adjusted width of the wrapPanel is. This way the 4th item will be placed on the 2nd row if the ColumnCount is 3. -->
<WrapPanel.ItemWidth>
<MultiBinding Converter="{StaticResource itemWidthConverter}">
<Binding Path="ColumnCount"/>
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</WrapPanel.ItemWidth>
</WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ctrls:SpecialGroupBox>
</DataTemplate>
<DataTemplate x:Key="listViewTemplate">
<ctrls:SpecialControl/>
</DataTemplate>
<local:GuiTemplateSelector
... <!--More templates -->
GroupBoxTemplate="{StaticResource groupBoxTemplate}"
SpecialTemplate="{StaticResource listViewTemplate}"
x:Key="guiTemplateSelector"/>
</UserControl.Resources>
<DockPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto" Grid.Row="1">
<StackPanel Name="rootPanel" DockPanel.Dock ="Top" Width="Auto" Height="Auto">
<ItemsControl ItemsSource="{Binding guiItemsList}" ItemTemplateSelector=" {StaticResource guiTemplateSelector}">
</ItemsControl>
</StackPanel>
</ScrollViewer>
</DockPanel>
</UserControl>
If you're wondering what the SpecialGroupBox does, it holds other controls, positioning them a certain way. There are a few of them in this window, and they work. It is inside of one of these SpecialGroupBoxes that my SpecialControl is supposed to appear.
Does your UserControl have a corresponding .xaml.cs file? This problem seems to happen when it doesn't.
If you're using Visual Studio, try copying all the XAML from your UserControl, then deleting it from your project, adding a new UserControl (with the same name as before), then pasting your content back into the XAML file. This will ensure that you have the correct .xaml.cs file set up.
See this answer.
In your post you didn't mention how you are using the listViewTemplate . if your using inside listview/listbox you can try like this it will load the user control:
<ListView ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate >
<ContentControl ContentTemplate="{StaticResource listviewDataTemplate}" Content="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Applying DataTemplate based on type

when i do the following and set the content of a Control to my ViewModel the Template gets applied automatically.
<DataTemplate DataType="{x:Type ViewModels:ViewModel}">
<StackPanel Orientation="Vertical">
<ControlX ....>
<ControlY ....>
</StackPanel>
</DataTemplate>
however i want to use FindResource to get the DataTemplate in the code behind so i had to add an x:key
<DataTemplate DataType="{x:Type ViewModels:ViewModel}" x:Key="{x:Type ViewModels:ViewModel}">
<DataTemplate DataType="{x:Type ViewModels:ViewModel}" x:Key="ViewModelTemplate">
But when i add an x:key the FindResource() works but the DataTemplate stops being applied automatically based on the type, any workaround available for this?
As a bad looking work around, you may try creating 2 DataTemplates that share the same content:
This ControlTemplate defines the shared content:
<ControlTemplate TargetType="{x:Type ContentControl}"
x:Key="MyControlTemplate">
<TextBlock Text="Some content" />
</ControlTemplate>
Then 2 DataTemplates as a workaround:
<DataTemplate x:Key="MyDataTemplate">
<ContentControl Template="{StaticResource MyControlTemplate}" />
</DataTemplate>
<DataTemplate DataType="{x:Type system:String}">
<ContentControl Template="{StaticResource MyControlTemplate}" />
</DataTemplate>
Edit:
I know that this answer came a year too late after I've provided a bad looking work around above, but it's better than never.
The implicit key set for an implicit data template is the data type itself wrapped in a DataTemplateKey.
You can either use:
FindResource(new DataTemplateKey(typeof (MainViewModel))
or
Resource[new DataTemplateKey(typeof (MainViewModel)]
to get your data template in code behind.
<DataTemplate DataType="{x:Type local:Task}">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
This DataTemplate gets applied automatically to all Task objects. Note that in this case the x:Key is set implicitly. Therefore, if you assign this DataTemplate an x:Key value, you are overriding the implicit x:Key and the DataTemplate would not be applied automatically.
It works as documented. AFAIK you can either use Key or DataType not both, there may be workarounds which I wasn't aware of.

Silverlight DataGrid styling

I have a DataGrid whose ItemsSource is bound to a changing Observable Collection. Inside of this collection is a Business Object. Based on some of the values of the Business Object's properties, I would like to be able to modify the color of the text for each item displayed in my DataGrid once the ItemsSource is created.
Has anyone done this before or ran across something similar? Thanks in advance.
<DataTemplate x:Key="MyTemplate">
<Grid x:Name="LayoutRoot">
<TextBlock Text="{Binding MyText}"
Foreground="{Binding MyStatus, Converter={StaticResource colorConverter}}" />
</Grid>
</DataTemplate>
I added the above code and inserted the TemplateColumn to the grid as below:
<data:DataGridTemplateColumn Header="Testing"
CellTemplate="{StaticResource MyTemplate}"/>
The code works fine and pulls out the correct text but the converter never fires and the Binding of the foreground is never called from the get on it.
Any ideas?
Yes. Use a Value Converter when databinding.
<UserControl.Resources>
<myconverters:BackColor x:Key="BackColor" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="{Binding SomeValue, Converter={StaticResource BackColor}" >
</Grid>
Then have your converter class implement IValueConverter and return a Brush object of some kind. You usually don't have to implement ConvertBack()
Adding to BC's answer:
You can make a DataGridTemplateColumn and specify a data template for cells in a column. In the data template you can bind the text colour.
<swcd:DataGrid ... >
<swcd:DataGrid.Columns>
<swcd:DataGridTemplateColumn Header="MyColumn" CellTemplate="{StaticResource MyColumnDataGridCellTemplate}"/>
...
in resources:
<DataTemplate x:Key="MyColumnDataGridCellTemplate">
<Grid>
<TextBlock Text="{Binding someproperty}" Foreground="{Binding someotherproperty, Converter={StaticResource MyConverter}}"/>
...

Categories

Resources