I have the following class which has an enum and an attached property.
namespace U.Helpers
{
public enum SearchDirection
{
Forward,
Backward
}
public class TargetedTriggerActionFindNextButton : TargetedTriggerAction<DataGrid>
{
protected override void Invoke(object parameter)
{
if (SearchDirectionControl == SearchDirection.Forward)
//Do something
else
//Do something else
}
public static readonly DependencyProperty SearchDirectionControlProperty =
DependencyProperty.Register("SearchDirectionControl", typeof(object), typeof(TargetedTriggerActionFindNextButton), new PropertyMetadata(SearchDirection.Forward));
public SearchDirection SearchDirectionControl
{
get { return (SearchDirection)GetValue(SearchDirectionControlProperty); }
set { SetValue(SearchDirectionControlProperty, value); }
}
}
}
Here is my XAML so far:
<UserControl x:Class="UM.LaunchPad"
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:helpers="clr-namespace:UM.Helpers">
<Grid Name="gridUsers" Background="Transparent">
<Button Name="SearchNextButton" Content="Next" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<helpers:TargetedTriggerActionFindNextButton TargetObject="{Binding ElementName=GenericDataGrid}"
SearchDirectionControl="{Binding helpers:SearchDirection.Forward}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</UserControl>
I am wondering how to add an enum value to my attached property on the XAML side. See the button's SearchDirectionControl.
Do not use a binding when you want to refer to a specific Enum value, but use the x:Static Markup Extension instead:
<helpers:TargetedTriggerActionFindNextButton
TargetObject="{Binding ElementName=GenericDataGrid}"
SearchDirectionControl="{x:Static helpers:SearchDirection.Forward}"
/>
Quoting the MSDN documentation of x:Static:
References any static by-value code entity that is defined in a Common Language Specification (CLS)–compliant way.
[...]
The code entity that is referenced must be one of the following:
A constant
A static property
A field [sic; it should be a "static field", obviously]
An enumeration value
Related
I have created Attached Property, for learning purposes but can't get successful result.
public class AQDatagridDependencyProperties: DependencyObject
{
public static void SetCustomDataSource(AQDataGrid element, string value)
{
element.SetValue(CustomDataSourceProperty, value);
}
public static string GetCustomDataSource(AQDataGrid element)
{
return (string)element.GetValue(CustomDataSourceProperty);
}
// Using a DependencyProperty as the backing store for DataSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CustomDataSourceProperty =
DependencyProperty.Register("CustomDataSource", typeof(string), typeof(AQDataGrid), new PropertyMetadata("obuolys"));
}
I've placed this attached property in my custom datagrid User Control which is implemented in UserView Page.
<Page x:Class="PDB.UsersView"
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:PDB"
xmlns:PDB ="clr-namespace:PDBapi;assembly=PDBapi"
xmlns:Wpf ="clr-namespace:AQWpf;assembly=AQWpf"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="Users"
Name="Users"
VisualBitmapScalingMode="LowQuality"
>
<Page.DataContext>
<PDB:UsersViewModel x:Name="vm"/>
</Page.DataContext>
<Grid VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling">
<Wpf:AQDataGrid DataContext="{Binding AQDatagridViewModel}" Wpf:AQDatagridDependencyProperties.CustomDataSource="Something" />
</Grid>
Question is how to bind that attached property value inside Custom Datagrid User Control? Example:
<UserControl x:Class="AQWpf.AQDataGrid"
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:AQWpf"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
Name="AQCustomDataGrid"
>
<!--Custom Data grid Implementation-->
<DataGrid x:Name="InstructionsDataGrid"
Grid.Row="1"
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:AQDataGrid}, Path=DataContext}"
Style="{StaticResource OptimizedAGDatagrid}"
ItemsSource="{Binding Data}"
CurrentItem="{Binding SelectedObject, Mode=TwoWay}"
CurrentColumn="{Binding CurrentColumn, Mode=TwoWay}"
CurrentCell="{Binding CurrentCells, Mode=TwoWay}"
Tag="<----How to bind here? ---->}"
>
Your attached property declaration is incorrect. You must call RegisterAttached instead of Register, and the third argument passed to the method must by the type of the class that declares the property.
Besides that, the declaring class does not need to be derived from DependencyObject, and could even be declared static:
public static class AQDatagridDependencyProperties
{
public static readonly DependencyProperty CustomDataSourceProperty =
DependencyProperty.RegisterAttached( // here
"CustomDataSource",
typeof(string),
typeof(AQDatagridDependencyProperties), // and here
new PropertyMetadata("obuolys"));
public static string GetCustomDataSource(AQDataGrid element)
{
return (string)element.GetValue(CustomDataSourceProperty);
}
public static void SetCustomDataSource(AQDataGrid element, string value)
{
element.SetValue(CustomDataSourceProperty, value);
}
}
You would set that property like
<local:AQDataGrid local:AQDatagridDependencyProperties.CustomDataSource="something" >
and bind to it by an expression like
Tag="{Binding Path=(local:AQDatagridDependencyProperties.CustomDataSource),
RelativeSource={RelativeSource AncestorType=UserControl}}"
As a note, you would typically declare the property as a regular dependency property in the AQDataGrid class.
You were defining a simple DepencyProperty. You have to use the DependencyProperty.RegisterAttached method to register the DependencyProperty as an attached property.
Also the owner type must be set to the declaring class' type (typeof(AQDatagridDependencyProperties)) and not the attaching type (typeof(AQDataGrid)):
AQDatagridDependencyProperties.cs
public class AQDatagridDependencyProperties : DependencyObject
{
public static readonly DependencyProperty CustomDataSourceProperty = DependencyProperty.RegisterAttached(
"CustomDataSource",
typeof(string),
typeof(AQDatagridDependencyProperties),
new FrameworkPropertyMetadata("obuolys", AQDatagridDependencyProperties.DebugPropertyChanged));
private static void DebugPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var oldValue = e.OldValue; // Set breakpoints here
var newValue = e.NewValue; // in order to track property changes
}
public static void SetCustomDataSource(DependencyObject attachingElement, string value)
{
attachingElement.SetValue(CustomDataSourceProperty, value);
}
public static string GetCustomDataSource(DependencyObject attachingElement)
{
return (string) attachingElement.GetValue(CustomDataSourceProperty);
}
}
Usage
<AQDataGrid AQDatagridDependencyProperties.CustomDataSource="Something" />
Inside the AQDataGrid control:
<DataGrid x:Name="InstructionsDataGrid"
Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=AQDataGrid}, Path=(AQDatagridDependencyProperties.CustomDataSource)}" />
An app I'm working on requires a ConverterParameter to be an enum. For this, the regular way to do would be:
{Binding whatever,
Converter={StaticResource converterName},
ConverterParameter={x:Static namespace:Enum.Value}}
However, the UWP platform x: namespace does not seem to have the Static extension.
Does anyone know if there's a solution that does not rely on x:Static for comparing an enum in binding?
This works for me in a UWP:
<Button Command="{Binding CheckWeatherCommand}">
<Button.CommandParameter>
<local:WeatherEnum>Cold</local:WeatherEnum>
<Button.CommandParameter>
</Button>
The most concise way I know of...
public enum WeatherEnum
{
Cold,
Hot
}
Define the enum value in XAML:
<local:WeatherEnum x:Key="WeatherEnumValueCold">Cold</local:WeatherEnum>
And simply use it:
"{Binding whatever, Converter={StaticResource converterName},
ConverterParameter={StaticResource WeatherEnumValueCold}}"
There is no Static Markup Extension on UWP (and WinRT platform too).
One of the possible solutions is to create class with enum values as properties and store instance of this class in the ResourceDictionary.
Example:
public enum Weather
{
Cold,
Hot
}
Here is our class with enum values:
public class WeatherEnumValues
{
public static Weather Cold
{
get
{
return Weather.Cold;
}
}
public static Weather Hot
{
get
{
return Weather.Hot;
}
}
}
In your ResourceDictionary:
<local:WeatherEnumValues x:Key="WeatherEnumValues" />
And here we are:
"{Binding whatever, Converter={StaticResource converterName},
ConverterParameter={Binding Hot, Source={StaticResource WeatherEnumValues}}}" />
This is an answer utilizing resources and without Converters:
View:
<Page
.....
xmlns:local="using:EnumNamespace"
.....
>
<Grid>
<Grid.Resources>
<local:EnumType x:Key="EnumNamedConstantKey">EnumNamedConstant</local:SettingsCats>
</Grid.Resources>
<Button Content="DoSomething" Command="{Binding DoSomethingCommand}" CommandParameter="{StaticResource EnumNamedConstantKey}" />
</Grid>
</Page>
ViewModel
public RelayCommand<EnumType> DoSomethingCommand { get; }
public SomeViewModel()
{
DoSomethingCommand = new RelayCommand<EnumType>(DoSomethingCommandAction);
}
private void DoSomethingCommandAction(EnumType _enumNameConstant)
{
// Logic
.........................
}
At work I have several pages, each with buttons in the same places, and with the same properties. Each page also has minor differences. To that end, we created a userControl Template and put all the buttons in it, then applied that user control to all the pages. However, now it's rather hard to access the buttons and modify them from each page's xaml, because they are inside a UserControl on the page..... How do I elegantly access the buttons from each page?
What I've tried:
Currently, we bind to a bunch of dependency properties. I don't like this option because I have a lot of buttons, and need to control a lot of properties on those buttons. The result is hundreds of dependency properties, and a real mess to wade through when we need to change something.
Another method is to use styles. I like this method generally, but because these buttons are inside another control it becomes difficult to modify them, and the template would only be exactly right for one button, at one time.
Adam Kemp posted about letting the user just insert their own button here, and this is the method I'm currently trying to impliment / modify. Unfortunately, I don't have access to Xamarin.
Although the template is inserted when the code runs, the template is not updating the button correctly. If I put a breakpoint in the MyButton Setter, I can see that value is actually an empty button, rather than the one I assigned in my main window. How do I fix this?
Here's some simplified Code:
My Template UserControl's xaml:
<UserControl x:Class="TemplateCode.Template"
x:Name="TemplatePage"
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="350"
d:DesignWidth="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Background="DarkGray">
<Grid>
<Button x:Name="_button" Width="200" Height="100" Content="Template Button"/>
</Grid>
</UserControl>
My Template UserControl's Code Behind:
using System.Windows.Controls;
namespace TemplateCode
{
public partial class Template : UserControl
{
public static Button DefaultButton;
public Template()
{
InitializeComponent();
}
public Button MyButton
{
get
{
return _button;
}
set
{
_button = value; //I get here, but value is a blank button?!
// Eventually, I'd like to do something like:
// Foreach (property in value)
// {
// If( value.property != DefaultButton.property) )
// {
// _button.property = value.property;
// }
// }
// This way users only have to update some of the properties
}
}
}
}
And now the application where I want to use it:
<Window x:Class="TemplateCode.MainWindow"
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"
xmlns:templateCode="clr-namespace:TemplateCode"
Title="MainWindow"
Height="350"
Width="525"
Background="LimeGreen"
DataContext="{Binding RelativeSource={RelativeSource Self}}" >
<Grid>
<templateCode:Template>
<templateCode:Template.MyButton>
<Button Background="Yellow"
Content="Actual Button"
Width="200"
Height="100"/>
</templateCode:Template.MyButton>
</templateCode:Template>
</Grid>
</Window>
And Now the Code Behind:
Using System.Windows;
Namespace TemplateCode
{
Public partial class MainWindow : Window
{
Public MainWindow()
{
InitializeComponent();
}
}
}
Edit: While I want to remove unnecessary dependency properties in the template userControl, I'd still like to set bindings on the button's properties from the XAML.
rather than use many dependency properties, prefer style approach. Style contains every property available for a Button control.
I would create a DependencyProperty for each button style in UserControl.
public partial class TemplateUserControl : UserControl
{
public TemplateUserControl()
{
InitializeComponent();
}
public static readonly DependencyProperty FirstButtonStyleProperty =
DependencyProperty.Register("FirstButtonStyle", typeof (Style), typeof (TemplateUserControl));
public Style FirstButtonStyle
{
get { return (Style)GetValue(FirstButtonStyleProperty); }
set { SetValue(FirstButtonStyleProperty, value); }
}
public static readonly DependencyProperty SecondButtonStyleProperty =
DependencyProperty.Register("SecondButtonStyle", typeof (Style), typeof (TemplateUserControl));
public Style SecondButtonStyle
{
get { return (Style)GetValue(SecondButtonStyleProperty); }
set { SetValue(SecondButtonStyleProperty, value); }
}
}
and then modify xaml for buttons to pick these styles:
<UserControl x:Class="MyApp.TemplateUserControl"
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="200" d:DesignWidth="300"
Background="DarkGray">
<StackPanel>
<Button x:Name="_button" Width="200" Height="100"
Style="{Binding Path=FirstButtonStyle, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<Button x:Name="_button2" Width="200" Height="100"
Style="{Binding Path=SecondButtonStyle, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</StackPanel>
</UserControl>
now when buttons have to be customized, that can achieved by custom styles:
<StackPanel>
<StackPanel.Resources>
<!--common theme properties-->
<Style TargetType="Button" x:Key="TemplateButtonBase">
<Setter Property="FontSize" Value="18"/>
<Setter Property="Foreground" Value="Blue"/>
</Style>
<!--unique settings of the 1st button-->
<!--uses common base style-->
<Style TargetType="Button" x:Key="BFirst" BasedOn="{StaticResource TemplateButtonBase}">
<Setter Property="Content" Value="1st"/>
</Style>
<Style TargetType="Button" x:Key="BSecond" BasedOn="{StaticResource TemplateButtonBase}">
<Setter Property="Content" Value="2nd"/>
</Style>
</StackPanel.Resources>
<myApp:TemplateUserControl FirstButtonStyle="{StaticResource BFirst}"
SecondButtonStyle="{StaticResource BSecond}"/>
</StackPanel>
You could register a Dependency Property Button on your UserControland handle the initialization in its PropertyChangedCallback.
Template.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Windows.Markup.Primitives;
namespace TemplateCode
{
public partial class Template : UserControl
{
public Template()
{
InitializeComponent();
}
public static readonly DependencyProperty ButtonProperty =
DependencyProperty.Register("Button", typeof(Button), typeof(Template),
new UIPropertyMetadata(new PropertyChangedCallback(ButtonChangedCallback)));
public Button Button
{
get { return (Button)GetValue(ButtonProperty); }
set { SetValue(ButtonProperty, value); }
}
public static List<DependencyProperty> GetDependencyProperties(Object element)
{
List<DependencyProperty> properties = new List<DependencyProperty>();
MarkupObject markupObject = MarkupWriter.GetMarkupObjectFor(element);
if (markupObject != null)
{
foreach (MarkupProperty mp in markupObject.Properties)
{
if (mp.DependencyProperty != null)
{
properties.Add(mp.DependencyProperty);
}
}
}
return properties;
}
private static void ButtonChangedCallback(object sender, DependencyPropertyChangedEventArgs args)
{
// Get button defined by user in MainWindow
Button userButton = (Button)args.NewValue;
// Get template button in UserControl
UserControl template = (UserControl)sender;
Button templateButton = (Button)template.FindName("button");
// Get userButton props and change templateButton accordingly
List<DependencyProperty> properties = GetDependencyProperties(userButton);
foreach(DependencyProperty property in properties)
{
if (templateButton.GetValue(property) != userButton.GetValue(property))
{
templateButton.SetValue(property, userButton.GetValue(property));
}
}
}
}
}
Template.xaml
UserControl DataContext is inherited from parent, no need not to set it explicitly
<UserControl x:Class="TemplateCode.Template"
x:Name="TemplatePage"
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="350"
d:DesignWidth="525"
Background="DarkGray">
<Grid>
<Button x:Name="button" Width="200" Height="100" Content="Template Button"/>
</Grid>
</UserControl>
MainWindow.xaml
You were setting Button.Content instead of Button
<Window x:Class="TemplateCode.MainWindow"
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"
xmlns:templateCode="clr-namespace:TemplateCode"
Title="MainWindow"
Height="350"
Width="525">
<Window.Resources>
<Button x:Key="UserButton"
Background="Yellow"
Content="Actual Button"
Width="200"
Height="100"
/>
</Window.Resources>
<Grid>
<templateCode:Template Button="{StaticResource UserButton}"/>
</Grid>
</Window>
EDIT - Binding Button.Content
3 ways to do this:
1. Dependency Properties
By far the best method. Creating UserControl DP's for every property on the Button is certainly overkill, but for those you want bound to the ViewModel / MainWindow DataContext it makes sense.
Adding in Template.xaml.cs
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(Template));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
Template.xaml
<UserControl x:Class="TemplateCode.Template"
...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Button x:Name="button" Width="200" Height="100" Content="{Binding Text}"/>
</Grid>
</UserControl>
MainWindow.xaml
<Window.Resources>
<Button x:Key="UserButton"
Background="Yellow"
Width="200"
Height="100"
/>
</Window.Resources>
<Grid>
<templateCode:Template
Button="{StaticResource UserButton}"
Text="{Binding DataContext.Txt,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</Grid>
Or
<Window.Resources>
<Button x:Key="UserButton"
Background="Yellow"
Content="Actual Button"
Width="200"
Height="100"
/>
</Window.Resources>
<Grid>
<templateCode:Template
Button="{StaticResource UserButton}"/>
</Grid>
Value precedence: UserButton Content > DP Text, so setting the Content in Resources wins.
2. Creating the Button in your ViewModel
MVVM purists won't like this, but you could use the Binding mark up instead of StaticResource.
MainWindow.xaml
<Grid>
<templateCode:Template
Button="{Binding DataContext.UserButton,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</Grid>
3. Setting the binding in code
As you already noticed, a ViewModel prop (e.g. Txt) can't be referenced in Resources because of the order everything is initialized. You can still do it in code later, but it gets a bit messy with the error to prove.
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor,
AncestorType='System.Windows.Window', AncestorLevel='1''.
BindingExpression:Path=DataContext.Txt; DataItem=null; target element
is 'Button' (Name=''); target property is 'Content' (type 'Object')
Note you need to define the full path on the Content property (setting DataContext on parent won't do).
MainWindow.xaml
<Window.Resources>
<Button x:Key="UserButton"
Background="Yellow"
Width="200"
Height="100"
Content="{Binding DataContext.Txt,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
/>
</Window.Resources>
<Grid>
<templateCode:Template Button="{StaticResource UserButton}"/>
</Grid>
Template.xaml.cs
private static void ButtonChangedCallback(object sender, DependencyPropertyChangedEventArgs args)
{
// Get button defined by user in MainWindow
Button userButton = (Button)args.NewValue;
// Get template button in UserControl
UserControl template = (UserControl)sender;
Button templateButton = (Button)template.FindName("button");
// Get userButton props and change templateButton accordingly
List<DependencyProperty> properties = GetDependencyProperties(userButton);
foreach (DependencyProperty property in properties)
{
if (templateButton.GetValue(property) != userButton.GetValue(property))
templateButton.SetValue(property, userButton.GetValue(property));
}
// Set Content binding
BindingExpression bindingExpression = userButton.GetBindingExpression(Button.ContentProperty);
if (bindingExpression != null)
templateButton.SetBinding(Button.ContentProperty, bindingExpression.ParentBinding);
}
If you can group your changes to your buttons to one or multiple properties on your datacontext, you could work with DataTriggers:
<Button x:Name="TestButton">
<Button.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsButtonEnabled}" Value="True">
<Setter TargetName="TestButton" Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
You can even use multiple conditions with MultiDataTriggers.
The main problem is that Template components are initialized before mainwindow components.I mean all properties of the button in mainwindow are set after the button in template class is initialized. Therefore, as you said value sets to null. All I want to say is about sequence of initializing objects.If you make a trick such a way as follows ;
public partial class Template : UserControl
{
private Button _btn ;
public Template()
{
}
public Button MyButton
{
get
{
return _button;
}
set
{
_btn = value;
_button = value;
}
}
protected override void OnInitialized(EventArgs e)
{
InitializeComponent();
base.OnInitialized(e);
this._button.Content = _btn.Content;
this._button.Background = _btn.Background;
this.Width = _btn.Width;
this.Height = _btn.Height;
}
}
It is going to work undoubtly.
Another Option based on #Funk's answer is to make a content control instead of a button on the template, then bind the content control's content to your ButtonProperty in the code behind:
on the template:
<ContentControl Content={Binding myButton} Width="200" Height="100"/>
in the template code behind:
public static readonly DependencyProperty myButtonProperty =
DependencyProperty.Register("Button", typeof(Button), typeof(Template),
new UIPropertyMetadata(new PropertyChangedCallback(ButtonChangedCallback)));
and then on the Main Window:
<Window.Resources>
<Button x:Key="UserButton"
Background="Yellow"
Content="Actual Button"
/>
</Window.Resources>
<Grid>
<templateCode:Template myButton="{StaticResource UserButton}"/>
</Grid>
The nice thing about this is that Visual Studio is smart enough to show this code at design time, as well as having less code overall.
You can set things constant things (like location, font, and coloring) for your button either on the content control or in a default style, and then modify just the parts you need for you button.
One option is to simply start writing C# on the xaml page using < ![CDATA[ *** ]]>
In the Main Window.xaml you change to:
<templateCode:Template x:Name="test">
<x:Code><![CDATA[
Void OnStartup()
{
test.MyButton.Content="Actual Button";
test.MyButton.Background = new SolidColorBrush(Color.FromArgb(255,255,255,0));
}
]]>
</x:Code>
Then right after Initialize Object() you call OnStartup().
Although this does let you edit specific properties in the xaml, this is about the same as just writing the code in the code behind, where others expect it to be.
An app I'm working on requires a ConverterParameter to be an enum. For this, the regular way to do would be:
{Binding whatever,
Converter={StaticResource converterName},
ConverterParameter={x:Static namespace:Enum.Value}}
However, the UWP platform x: namespace does not seem to have the Static extension.
Does anyone know if there's a solution that does not rely on x:Static for comparing an enum in binding?
This works for me in a UWP:
<Button Command="{Binding CheckWeatherCommand}">
<Button.CommandParameter>
<local:WeatherEnum>Cold</local:WeatherEnum>
<Button.CommandParameter>
</Button>
The most concise way I know of...
public enum WeatherEnum
{
Cold,
Hot
}
Define the enum value in XAML:
<local:WeatherEnum x:Key="WeatherEnumValueCold">Cold</local:WeatherEnum>
And simply use it:
"{Binding whatever, Converter={StaticResource converterName},
ConverterParameter={StaticResource WeatherEnumValueCold}}"
There is no Static Markup Extension on UWP (and WinRT platform too).
One of the possible solutions is to create class with enum values as properties and store instance of this class in the ResourceDictionary.
Example:
public enum Weather
{
Cold,
Hot
}
Here is our class with enum values:
public class WeatherEnumValues
{
public static Weather Cold
{
get
{
return Weather.Cold;
}
}
public static Weather Hot
{
get
{
return Weather.Hot;
}
}
}
In your ResourceDictionary:
<local:WeatherEnumValues x:Key="WeatherEnumValues" />
And here we are:
"{Binding whatever, Converter={StaticResource converterName},
ConverterParameter={Binding Hot, Source={StaticResource WeatherEnumValues}}}" />
This is an answer utilizing resources and without Converters:
View:
<Page
.....
xmlns:local="using:EnumNamespace"
.....
>
<Grid>
<Grid.Resources>
<local:EnumType x:Key="EnumNamedConstantKey">EnumNamedConstant</local:SettingsCats>
</Grid.Resources>
<Button Content="DoSomething" Command="{Binding DoSomethingCommand}" CommandParameter="{StaticResource EnumNamedConstantKey}" />
</Grid>
</Page>
ViewModel
public RelayCommand<EnumType> DoSomethingCommand { get; }
public SomeViewModel()
{
DoSomethingCommand = new RelayCommand<EnumType>(DoSomethingCommandAction);
}
private void DoSomethingCommandAction(EnumType _enumNameConstant)
{
// Logic
.........................
}
I created a button whose commandparameter is set and command using a class that implements ICommand interface. But my button is disabled. Why is that? I got this code from here: ICommand is like a chocolate cake
<Window x:Class="ICommand_Implementation_CSharp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ICommand_Implementation_CSharp"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid>
<Grid.Resources>
<local:HelloWorldCommand x:Key="hwc" />
</Grid.Resources>
<Button Command="{StaticResource hwc}" CommandParameter="Hello"
Height="23" HorizontalAlignment="Left" Margin="212,138,0,0"
Name="Button1" VerticalAlignment="Top" Width="75">Button</Button>
</Grid>
</Grid>
and my class is
class HelloWorldCommand:ICommand
{
public bool CanExecute(object parameter)
{
return parameter != null;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show(parameter.ToString());
}
}
Well, this is very-very simple implementation of ICommand.
As #JleruOHeP says, partially problem can be solved by swapping setters of Command and CommandParameter. But this is ugly way, because you have to remember the sequence every time.
More correct way is to tell CommandManager to re-query states of command:
public class HelloWorldCommand : ICommand
{
public bool CanExecute(object parameter)
{
return parameter != null;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
MessageBox.Show(parameter.ToString());
}
}
Now the sequence of setters is indifferent.
To understand how CommandManager works, you can read this nice article from Josh Smith.
The simplest answer - switch places of Command and Command parameter:
<Button CommandParameter="Hello" Command="{StaticResource hwc}" .../>
But better one is given by #Dennis
In my case it was the CommandParameter type that was causing the issue. My button was simply bound like this:
<Button Content="New" Command="{Binding NewCommand}" CommandParameter="False" />
The underlying NewCommand is a RelayCommand<bool>. Somehow XAML was not able to translate False to bool. (Note that it does work for many built-in types and properties; maybe some TypeConverter or something at action there).
Solution was to simply spoon-feed XAML about the real underlying type of CommandParameter, like this:
<Button Content="New" Command="{Binding NewCommand}">
<Button.CommandParameter>
<sys:Boolean>
False
</sys:Boolean>
</Button.CommandParameter>
</Button>
You have to import sys namespace at the top of your XAML file, like this:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Hope this helps someone down the road.