WPF/Silverlight: How to add a command to a modified control template? - c#

I have overridden a control template for a certain 3rd party control that I am using:
So
<Style TargetType="Some3rdPartyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Some3rdPartyControl">
<Grid>
...alot of stuff..
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Basically, I have added a button inside the control template:
<Style TargetType="Some3rdPartyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Some3rdPartyControl">
<Grid>
<Button x:Name="myButton" Width="20" Height="20">
...alot of stuff..
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Basically, I'd like to hook up an event, err probably a command to this new button... How do I do this? Which type of command is it, and where would it live? I don't have access to the actual .cs class of this third party control.So I'm hoping this command could live somewhere in a file that I generate, is this even possible?

You can have your command in your ViewModel and can bind that command to the button by simply using the RealtiveSource for binding like this -
<Style TargetType="Some3rdPartyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Some3rdPartyControl">
<Grid>
<Button x:Name="myButton" Width="20" Height="20"
Command="{Binding DataContext.YourCommandName, RelativeSource={RealtiveSource FindAncestor, AncestorType={x:Type UserControl}}}">
...alot of stuff..
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Do you want a Command Property for your UserControl, or a static Command Definition?
If you want a Command Property, such as the Button.Command property, you need to create your own command definition in the code behind your UserControl. Look up creating your own custom DependencyProperty for that. The command that gets executed would "live" in whatever your DataContext was and the XAML code would look something like this:
<local:MyCustomControl SomeCustomCommand="{Binding SomeCommand}" />
If it's going to be a static Command Definition, like Copy or Paste commands, you need to create the static ICommand somewhere that the XAML can access, and bind the XAML with something like this:
<Button Command="{x:Static local:SomeCustomCommand}" />

This Command comes from the templated parent i.e. Some3rdPartyControl ...
Does your Some3rdPartyControl has a Command type depepdency property in it? If yes then you can do a TemplateBinding to that.
<Button x:Name="myButton" Command="{TemplateBinding Command}" Width="20" Height="20">
Else you will have to create a Command type attached property to your Some3rdPartyControl and use that to TemplateBinding to the Button.
<Style TargetType="Some3rdPartyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Some3rdPartyControl">
<Grid>
<Button x:Name="myButton" Width="20" Height="20" Command="{TemplateBinding myNamespace:MyAttachedBehavior.MyAttachedCommand}">
...alot of stuff..
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now while implementing your Some3rdPartyControl do this...
<Some3rdPartyControl myNamespace:MyAttachedBehavior.MyAttachedCommand="{Binding CommandFromDataContextViewModel}" />
CommandFromDataContextViewModel is your custom command. Due to TemplateBinding it reaches the Button and when Button click happnes this CommandFromDataContextViewModel command executes.
Does this answer your question?

Related

MahApps MetroProgressBar does not work inside ControlTemplate

I would like to use MetroProgressBar in my UserControl.
<UserControl x:Class="WpfApplication7.UserControl1">
<StackPanel Background="#ccc">
<controls:MetroProgressBar IsIndeterminate="True"/>
</StackPanel>
</UserControl>
It works fine. But now I need to support external content in the user control.
So I created a new one "UserControl2" to demo:
<UserControl x:Class="WpfApplication7.UserControl2">
<UserControl.Resources>
<Style TargetType="{x:Type local:UserControl2}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:UserControl2}">
<StackPanel Background="#ccc">
<controls:MetroProgressBar IsIndeterminate="True"/>
<!--<ContentPresenter/>-->
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
</UserControl>
Then I put the both controls to the form:
<StackPanel>
<local:UserControl1 Background="#ccc"/>
<local:UserControl2 Background="#ccc" Margin="0,6,0,0"/>
</StackPanel>
As result I see that UserControl2 does not show Progress Bar.
How can I fix it?
Note: In the designer UserControl2 is rendered as expected with progress bar.
In your style for UserControl2, set properties EllipseDiameter and EllipseOffset to some value (default is 4), as shown below:
<Style TargetType="{x:Type local:UserControl2}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:UserControl2}">
<StackPanel Background="#ccc">
<controls:MetroProgressBar EllipseDiameter="12"
EllipseOffset="12"
IsIndeterminate="True"/>
<!--<ContentPresenter/>-->
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Default ValidationErrorTemplate for my custom DataGridRowHeader style

I've had the following problem and solved it with a modified version of synergetic's answer. So my style looks like this:
<Style x:Key="{x:Type DataGridRowHeader}" TargetType="{x:Type DataGridRowHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Control SnapsToDevicePixels="true"
Visibility="{Binding RelativeSource={RelativeSource
AncestorType={x:Type DataGridRow}},
Path=Item.HasErrors, Converter={StaticResource
BooleanToVisibilityConverter}}"
Template="{Binding RelativeSource={RelativeSource
AncestorType={x:Type DataGridRow}},
Path=ValidationErrorTemplate}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It works well, but the DataGridRowHeader isn't shown in the default style anymore although (as I understand it) the default style is refered via ValidationErrorTemplate.
Compare
(after adding the aforementioned style) with
that I want to achieve.
Any idea what I need to change to get that defaul style? I'm out of ideas.

WPF XAML display content in a ContentControl

I need to display a number in a square, centered horizontally and vertically.
When I tried to use a label for that purpose, it seemed like it ignored the centering completely. So I decided to use a grid and display a label on the grid as that centers perfectly.
I need to use a template as there's several themes available. From what I've found on the internet, I thought this ( ignoring the centering for now )
<ControlTemplate x:Key="ClockTemplate">
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Background" Value="White"/>
</Style>
</Grid.Style>
<Label>
<Label.Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="#376092"/>
</Style>
</Label.Style>
<ContentPresenter/>
</Label>
</Grid>
</ControlTemplate>
would be correct. Using it as follows:
<ContentControl Content="20" Height="64" Width="64" Template="{DynamicResource ClockTemplate}"/>
the content is not displayed tho, what am I doing wrong? Also, is there a better way to achieve my goal?
As per my understanding this is not the correct approach. Instead of creating ControlTemplate you have to write a Style for your control like below, also use StaticResource binding if possible. It is faster than Dynamic binding. Please not that, I have not mentioned the Label size inside the ControlTemplate. Please do it based on your needs
<Style x:Key="ContentControlStyle"
TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Grid Background="White">
<Label Foreground="#376092"
Width="200"
Height="100" Content="{TemplateBinding Content}">
</Label>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
bind your ContentControl with the newly created Style like below
<ContentControl Style="{StaticResource ContentControlStyle} ">
If your requirement is only to set some value in ContentControl, use Label instead and change the Style of the Label. Because ContentControl is heavy

WPF content control styling

I have a custom control which is basically a contentcontrol
public class PromoAlarmBox : ContentControl
{
static PromoAlarmBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PromoAlarmBox), new FrameworkPropertyMetadata(typeof(PromoAlarmBox)));
}
}
I add it to the containing user control
<controls:PromoAlarmBox Grid.Row="9" Grid.Column="1" />
If I add the style to the containing usercontrols resources everything works fine
<UserControl.Resources>
<Style TargetType="{x:Type controls:PromoAlarmBox}">
<Setter Property="ContentControl.ContentTemplate">
<Setter.Value>
<DataTemplate >
<Rectangle Fill="Blue" Stroke="Black" Height="20" Width="20"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
But if I add it to generic.xaml in the custom controls project , nothing is show
<Style TargetType="{x:Type local:PromoAlarmBox}">
<Setter Property="ContentControl.ContentTemplate">
<Setter.Value>
<DataTemplate >
<Rectangle Fill="Blue" Stroke="Black" Height="20" Width="20"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
I know the style is applied as I have other controls in same project whos styles are defined in generic.xaml, Anyone have any ideas?
A simple static should do the trick...
static PromoAlarmBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PromoAlarmBox), new FrameworkPropertyMetadata(typeof(PromoAlarmBox)));
}
Although im not sure why there is a difference when you use a style as a local resource and when you use generic , this works for me
<Style TargetType="{x:Type local:PromoAlarmBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Rectangle VerticalAlignment="Stretch" Fill="Yellow" Stroke="Black" Height="20" Width="20"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

How I can hide a Button inside a Control Template?

How can I set Visibility="Visible" for the Button inside the Control Template when the IsSendBtnVisible property in the code-behind is true?
Here's my WPF page:
<Page
x:Class="CardViewPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CardViewPage">
<Grid Name="content" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DocumentViewer Margin="0" Grid.Row="0" Name="documentViewer" />
</Grid>
</Page>
Here's my Custom Template for the document viewer on this page:
<Style TargetType="{x:Type DocumentViewer}">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DocumentViewer}">
...
<Button
Click="btnSendToServer_Click"
Width="25"
Height="25"
Visibility="Collapsed" />
...
</ControlTemplate>
</Setter>
...
</Style>
U need to declare DependancyProperty for ur DocumentViewer and use TemplateBinding in xaml ControlTemplate (UrProperty for example)
<ControlTemplate TargetType="{x:Type DocumentViewer}">
...
<Button Click="btnSendToServer_Click"
Width="25"
Height="25"
Visibility="{TemplateBinding UrProperty}"
/>
...
</ControlTemplate>
I suggest you to use data triggers to achieve this...
<Button
Click="btnSendToServer_Click"
Width="25"
Height="25">
<Button.Style>
<Style>
<Setter Property="Button.Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSendBtnVisible}" Value="True">
<Setter Property="Button.Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Make sure to set visibility to collapsed in the style but not in the button properties..
Note: Binding for data trigger may change depending on your data context
Dima Martovoi,bathineni - thanks for replies.
Dima Martovoi, i think inherit from DocumentViewer is to hard for this small problem.
I tried to use variant with DataTrigger from bathineni's solution, but it's not works. Don't know, why.
Problem was solved using next binding:
<Button
Visibility="{Binding RelativeSource={RelativeSource AncestorType=Page},Path=SendToServerVisiblity}">
</Button>
where
public Visibility SendToServerVisiblity
{
get
{
if (IsOnlineMode)
return Visibility.Visible;
return Visibility.Collapsed;
}
}
in page code-behind

Categories

Resources