Change image of a templated button in wpf - wpf

I have a templated wpf button. I have to change the image at runtime.
xaml code:
<Window.Resources>
<Image x:Key="imgPlay" Source="Media/Knob Play.png"></Image>
<Image x:Key="imgStop" Source="Media/Knob Red.png"></Image>
<ControlTemplate x:Key="custom-button" TargetType="Button">
<Grid x:Name="btn_image">
<!--<Grid.Background>
<ImageBrush ImageSource="Media/Knob Red.png"></ImageBrush>
</Grid.Background>-->
<!--<Image Source="Media/Knob Red.png"></Image>-->
</Grid>
</ControlTemplate>
</Window.Resources>
button i need to change:
<Button Name="start" Template="{DynamicResource custom-button}" HorizontalAlignment="Left" Margin="147,67,0,0" VerticalAlignment="Top" Width="37" Height="30" Click="Start_Click">
<DynamicResource ResourceKey="imgStop"></DynamicResource>
</Button>
codebehind:
private void Start_Click(object sender, RoutedEventArgs e)
{
if (sw.IsRunning)
{
start.Content = FindResource("imgStop");
sw.Stop();
dt.Stop();
}
else
{
sw.Start();
dt.Start();
start.Content = FindResource("imgPlay");
}
}
tried many solutions in SO and net .Nothing worked.

Maybe you can get an inspiration from this...
Resource
<Window.Resources>
<ControlTemplate x:Key="option1" TargetType="Button">
<TextBlock Foreground="Red">ClickMe</TextBlock>
</ControlTemplate>
<ControlTemplate x:Key="option2" TargetType="Button">
<TextBlock Foreground="Green">Template is changed</TextBlock>
</ControlTemplate>
</Window.Resources>
XAML:
<Grid Width="100" Height="100">
<Button Name="theButton" Click="OnClick" Template="{StaticResource option1}"/>
</Grid>
Code behind:
private void OnClick(object sender, RoutedEventArgs e)
{
this.theButton.Template = (ControlTemplate)FindResource("option2");
}

Related

DragMove() will make the Border with cornerRadius lose its mouseover trigger state?

I have created a borderless window with rounded corners, and added the drag event and a trigger to it. Here is the simple code:
<Window x:Class="DebugTest.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:DebugTest"
mc:Ignorable="d" Height="200" Width="200"
AllowsTransparency="True" WindowStyle="None" Background="Transparent">
<Border x:Name="MainBorder" CornerRadius="15" Background="White" BorderBrush="Black" BorderThickness="1">
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MainBorder,Path=IsMouseOver}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Button Content="x" HorizontalAlignment="Right" VerticalAlignment="Top"
Margin="5" Height="20" Width="20" Click="Button_Click"/>
</Grid>
</Border>
</Window>
public MainWindow()
{
InitializeComponent();
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
this.DragMove();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
But when I run the exe file, click on the blank area within the window, the button will appear very obvious flickering situation.
Strangely enough, this situation hardly occurs when debugging in Visual Studio instead of double click the file, and it also doesn't happen while CornerRadius="0".
It looks like it lost the mouseover trigger on click, but I can't think of any good way to avoid flicker appearing, and to satisfy the need for both with rounded corners, draggable, and with trigger.
Well, although I don't know why only rounded corners would cause DragMove() to trigger the MouseLeave event, I circumvented this with background code instead of using xaml trigger.
<Border x:Name="MainBorder" CornerRadius="15" Background="White"
BorderBrush="Black" BorderThickness="1"
MouseEnter="MainBorder_MouseEnter" MouseLeave="MainBorder_MouseLeave">
<Grid Visibility="Hidden" x:Name="TriggerBorder">
<Button Content="x" HorizontalAlignment="Right" VerticalAlignment="Top"
Margin="5" Height="20" Width="20" Click="Button_Click"/>
</Grid>
</Border>
bool dragMoving = false;
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
dragMoving = true;
this.DragMove();
dragMoving = false;
}
private void MainBorder_MouseEnter(object sender, MouseEventArgs e)
{
TriggerBorder.Visibility = Visibility.Visible;
}
private void MainBorder_MouseLeave(object sender, MouseEventArgs e)
{
if (dragMoving) return;
TriggerBorder.Visibility = Visibility.Hidden;
}
Seems to work fine.

How to open a WPF Popup when RadioButton checked?

I have a RadioButton and a Popup. I want to open Popup when checked RadioButton and close Popup when LostFocus of it, without unchecked RadioButton.
I use this code.
<StackPanel>
<RadioButton x:Name="RadioButtonSave" IsChecked="{Binding IsSave}">Save</RadioButton>
<RadioButton x:Name="RadioButtonNotSave" LostFocus="RadioButtonNotSave_OnLostFocus" IsChecked="{Binding IsSave,Converter={StaticResource ToNegativeConverter}}">Not Save</RadioButton>
</StackPanel>
<Popup x:Name="Popup" IsOpen="{Binding IsChecked,ElementName=RadioButtonNotSave}" StaysOpen="True" Placement="Left" PlacementTarget="{Binding ElementName=RadioButtonNotSave}"></Popup>
private void RadioButtonNotSave_OnLostFocus(object sender, RoutedEventArgs e)
{
Popup.IsOpen = false;
}
It is open popup when checked but when lostfocuse unchecked radiobutton.
I set Mode=OneWay for IsOpen , it is open popup and don't unchecked radionButton in lost focuse, but it worked for one time.
Here is a way of doing it only in XAML:
<Window x:Class="RadioButtonAndPopup.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type RadioButton}">
<EventSetter Event="Click" Handler="EventSetter_OnHandler"/>
</Style>
</StackPanel.Resource>
<RadioButton x:Name="RadioBtn" Content="TestPopup"/>
<Popup x:Name="myPopup" IsOpen="{Binding IsChecked, ElementName=RadioBtn, Mode=OneWay}" Placement="Mouse" StaysOpen="False">
<Border Background="LightBlue">
<TextBlock>Popup</TextBlock>
</Border>
</Popup>
</StackPanel>
</Grid>
Setting StaysOpen false, you will be able to close the Popup by clicking anywhere outside it.
Update 1:
Add this in the StackPanel.Resources and name your Popup.
<Style TargetType="{x:Type RadioButton}">
<EventSetter Event="Click" Handler="EventSetter_OnHandler"/>
</Style>
And this will be added to codebehind:
private void EventSetter_OnHandler(object sender, RoutedEventArgs e)
{
myPopup.IsOpen = true;
}
So now the Popup will be opened on that Click event and not from the IsChecked binding.
Try this instead:
private void RadioButtonNotSave_OnLostFocus(object sender, RoutedEventArgs e)
{
IsChecked = false;
}

How to make my control focusable?

I have a control
public class FocusTestControl : Control {
public FocusTestControl() {
DefaultStyleKey = typeof(FocusTestControl);
}
}
Here is it's default style
<Style
TargetType="local:FocusTestControl">
<Setter
Property="Focusable"
Value="True" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Border
Background="AliceBlue" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I put this control on a window:
<Window
x:Class="MakeWpfControlFocusable.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MakeWpfControlFocusable"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition
Height="35" />
</Grid.RowDefinitions>
<local:FocusTestControl />
<StackPanel
Grid.Row="1"
Orientation="Horizontal">
<TextBlock
Text="Focused element: " />
<TextBlock
Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=KeyboardFocusedElement}" />
<TextBox
Text="Text" />
</StackPanel>
</Grid>
But clicking on control doesn't make it focused (I mean KebordFocus)
Actually, my task is handle the KeyDown and KeyUp events. But it is impossible when element has no keybord focus.
Keybord focus can be set by pessing Tab keyboard key. But is is not set when click on the control. I've subscribed to the MouseDown event and set focus manually in the handler.
public class FocusTestControl : Control, IInputElement {
public FocusTestControl() {
DefaultStyleKey = typeof(FocusTestControl);
MouseDown += FocusTestControl_MouseDown;
}
void FocusTestControl_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) {
Keyboard.Focus(this);
}
}

XAML to add header to radio button

So with a lot of looking around I am hoping to make a GroupBox that acts like a Radio button. The header section would act as the bullet. I took some code from this question
Styling a GroupBox
that is how I want it to look. But I want to have it as a Radio button. So I put in this code (mind you I've only been doing WPF for a week or 2 now)
<Style TargetType="{x:Type RadioButton}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<BulletDecorator>
<BulletDecorator.Bullet>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border x:Name="SelectedBorder"
Grid.Row="0"
Margin="4"
BorderBrush="Black"
BorderThickness="1"
Background="#25A0DA">
<Label x:Name="SelectedLabel" Foreground="Wheat">
<ContentPresenter Margin="4" />
</Label>
</Border>
<Border>
</Border>
</Grid>
</BulletDecorator.Bullet>
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="SelectedBorder" Property="Background" Value="PaleGreen"/>
<Setter TargetName="SelectedLabel"
Property="Foreground"
Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have a feeling that I can add a label to the second row of my grid, but then I don't know how to access it. I have that template in a test project in the Window.Resources section (I plan on moving it to a resource dictionary in my main project)
the xaml for my window is this
<Grid>
<GroupBox Name="grpDoor" Margin ="8" Grid.Row="0" Grid.Column="0">
<GroupBox.Header>
WPF RadioButton Template
</GroupBox.Header>
<StackPanel Margin ="8">
<RadioButton FontSize="15" Content="Dhaka" Margin="4" IsChecked="False"/>
<RadioButton FontSize="15" Content="Munshiganj" Margin="4" IsChecked="True" />
<RadioButton FontSize="15" Content="Gazipur" Margin="4" IsChecked="False" />
</StackPanel>
</GroupBox>
</Grid>
I then hoping for something like this (not sure how I'd do it yet though)
<Grid>
<GroupBox Name="grpDoor" Margin ="8" Grid.Row="0" Grid.Column="0">
<GroupBox.Header>
WPF RadioButton Template
</GroupBox.Header>
<StackPanel Margin ="8">
<RadioButton FontSize="15"
Content="Dhaka"
Margin="4"
IsChecked="False">
<RadioButton.Description>
This is a description that would show under my Header
</RadioButton.Description>
</RadioButton>
<RadioButton FontSize="15"
Content="Munshiganj"
Margin="4"
IsChecked="True">
<RadioButton.Description>
This is a description that would show under my Header
</RadioButton.Description>
</RadioButton>
<RadioButton FontSize="15"
Content="Gazipur"
Margin="4"
IsChecked="False">
<RadioButton.Description>
This is a description that would show under my Header
</RadioButton.Description>
</RadioButton>
</StackPanel>
</GroupBox>
</Grid>
Based on your clarification, here is a very simple example with a RadioButton that looks like a GroupBox.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:SimpleViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type local:SimpleOption}">
<RadioButton GroupName="choice" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}">
<RadioButton.Template>
<ControlTemplate TargetType="{x:Type RadioButton}">
<GroupBox x:Name="OptionBox" Header="{Binding Path=DisplayName, Mode=OneWay}">
<TextBlock Text="{Binding Path=Description, Mode=OneWay}"/>
</GroupBox>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, Mode=OneWay}" Value="True">
<Setter TargetName="OptionBox" Property="Background" Value="Blue"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</RadioButton.Template>
</RadioButton>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Path=Options, Mode=OneWay}"/>
</Grid>
</Window>
public class SimpleViewModel
{
public SimpleViewModel()
{
Options = new ObservableCollection<SimpleOption>();
var _with1 = Options;
_with1.Add(new SimpleOption {
DisplayName = "Dhaka",
Description = "This is a description for Dhaka."
});
_with1.Add(new SimpleOption {
DisplayName = "Munshiganj",
Description = "This is a description for Munshiganj.",
IsSelected = true
});
_with1.Add(new SimpleOption {
DisplayName = "Gazipur",
Description = "This is a description for Gazipur."
});
}
public ObservableCollection<SimpleOption> Options { get; set; }
}
public class SimpleOption : INotifyPropertyChanged
{
public string DisplayName {
get { return _displayName; }
set {
_displayName = value;
NotifyPropertyChanged("DisplayName");
}
}
private string _displayName;
public string Description {
get { return _description; }
set {
_description = value;
NotifyPropertyChanged("Description");
}
}
private string _description;
public bool IsSelected {
get { return _isSelected; }
set {
_isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
private bool _isSelected;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
}
I'd do it with a custom attached property. That way, you can bind to it from a ViewModel, or apply it directly in XAML.
First, create a new class in your Style assembly:
public static class RadioButtonExtender
{
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.RegisterAttached(
"Description",
typeof(string),
typeof(RadioButtonExtender),
new FrameworkPropertyMetadata(null));
[AttachedPropertyBrowsableForType(typeof(RadioButton))]
public static string GetDescription(RadioButton obj)
{
return (string)obj.GetValue(DescriptionProperty);
}
public static void SetDescription(RadioButton obj, string value)
{
obj.SetValue(DescriptionProperty, value);
}
}
And your style's Bullet would change so that the label is:
<Label x:Name="SelectedLabel"
Foreground="Wheat"
Content="{Binding (prop:RadioButtonExtender.Description), RelativeSource={RelativeSource TemplatedParent}} />
You could then use it in your final XAML:
<RadioButton FontSize="15"
Content="Dhaka"
Margin="4"
IsChecked="False">
<prop:RadioButtonExtender.Description>
This is a description that would show under my Header
</prop:RadioButtonExtender.Description>
</RadioButton>
As an added bonus, since you're creating the Style in a separate assembly, you can create a custom XAML namespace to make using your property easier.

Content control binding not working

When i set the content of the content control like below but the binding of the element inside the content get break.
i have given a content inside a property of a class and set the property as the content to the content control.
[Xmal]
<Grid>
<Button HorizontalAlignment="Center"
VerticalAlignment="Top"
Click="Button_Click_1"
Content="Click" />
<local:MyTile x:Name="mytile">
<local:MyTile.TileViewContent>
<StackPanel>
<TextBox x:Name="text"
Background="Red"
Text="MyText" />
<TextBox Text="{Binding ElementName=text, Path=Text,Mode=TwoWay}" />
</StackPanel>
</local:MyTile.TileViewContent>
</local:MyTile>
<ContentControl x:Name="contentcontrol" />
</Grid>
[C#]
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
contentcontrol.Content = mytile.TileViewContent;
}
}
public class MyTile:Control
{
public FrameworkElement TileViewContent
{
get { return (FrameworkElement)GetValue(TileViewContentProperty); }
set { SetValue(TileViewContentProperty, value); }
}
public static readonly DependencyProperty TileViewContentProperty =
DependencyProperty.RegisterAttached("TileViewContent", typeof(FrameworkElement), typeof(MyTile), new PropertyMetadata(null));
}
When i set the content the binding not working. please help
If you want to simply binding is works, not necessarily through ContentControl, use the Style for your element:
<Window.Resources>
<Style x:Key="MyTemplateForMyControl" TargetType="{x:Type local:MyTile}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyTile}">
<StackPanel>
<TextBox x:Name="MyTextBox" Text="MyText" Background="Red" />
<TextBox Text="{Binding ElementName=MyTextBox, Path=Text}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="Click" Click="Button_Click_1" />
<local:MyTile x:Name="MyTile" />
</Grid>
In code we are set the Style for your Control:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
MyTile.Style = this.Resources["MyTemplateForMyControl"] as Style;
}
If it is necessary to use ContentControl I can recommend instead of you Control use a DataTemplate:
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyDataForTemplate}">
<StackPanel>
<TextBox x:Name="MyTextBox" Text="{Binding TextBoxContent}" Background="Red" />
<TextBox Text="{Binding ElementName=MyTextBox, Path=Text}" />
</StackPanel>
</DataTemplate>
<!-- Some data -->
<local:MyDataForTemplate x:Key="MyDataForTile" TextBoxContent="MyText" />
</Window.Resources>
<Grid>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="Click" Click="Button_Click_1" />
<ContentControl Name="TileContentControl" />
</Grid>
There will be some data for a template:
public class MyDataForTemplate
{
string textBoxContent = "";
/// <summary>
/// Text for TextBox
/// </summary>
public string TextBoxContent
{
get
{
return textBoxContent;
}
set
{
textBoxContent = value;
}
}
}
In code we are set the data for a template:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
TileContentControl.Content = this.Resources["MyDataForTile"] as MyDataForTemplate;
}

Resources