WPF Custom Control - Button Event - c#

I start the WPF today, I'm trying to implement a custom control.
My problem is that I can not select an element in the template.
My code:
[Generic.xaml]
<Style x:Key="{x:Type local:ImageButton}" TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<StackPanel>
// -> My image
<Image Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}"></Image>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
[ImageButton.cs]
public class ImageButton : Button
{
public Image imageOff { get; set; }
public Image imageOn { get; set; }
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
public ImageButton()
: base()
{
this.MouseEnter += new MouseEventHandler(SetImageON);
}
public void SetImageON(object sender, MouseEventArgs e)
{
//Here i wanna change my image from StackPanel
}
}
Am I on the good way ? How can I change that image?

Give a name to your image, like x:Name="PART_Image". Then in your code behind override OnApplyTemplate method and store it into a variable.
private Image PART_Image;
public override void OnApplyTemplate()
{
PART_Image = this.GetTemplatedChild("PART_Image");
}
In this case you can access it in your method like:
this.PART_Image.Visibility = Visibility.Collapsed;

sender is your control so you could also right this.SetCurrentValue.
public void SetImageON(object sender, MouseEventArgs e)
{
ImageButton btn = (ImageButton)sender;
btn.SetCurrentValue(TagProperty,imageOn.Source);
}
it seems as though you might wan't to just save the source of your image in the control , instead of the entire image.
But if you wan't to use an Image object in code like this
xaml : in your template .
<StackPanel x:Name="myPanel" />
cs : in your control :
myPanel.Children.Add(imageOn);

You can achieve what you want with a Trigger:
<Style x:Key="{x:Type local:ImageButton}" TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<StackPanel>
<Image Source="{Binding Path=imageOff.Source, RelativeSource={RelativeSource TemplatedParent}}">
<Image.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Image.Source" Value="{Binding Path=imageOn.Source, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
</Image.Triggers>
</Image>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
That way you don't need the event in the code-behind. If you wanted to, you could make imageOff and imageOn DependencyProperties, that way you could define the source images in xaml too:
public class ImageButton : Button {
public static readonly DependencyProperty imageOffProperty = DependencyProperty.Register("imageOff", typeof(Image), typeof(ImageButton), new PropertyMetadata(null));
public Image imageOff {
get { return (Image)GetValue(imageOffProperty); }
set { SetValue(imageOffProperty, value); }
}
public static readonly DependencyProperty imageOnProperty = DependencyProperty.Register("imageOn", typeof(Image), typeof(ImageButton), new PropertyMetadata(null));
public Image imageOn {
get { return (Image)GetValue(imageOnProperty); }
set { SetValue(imageOnProperty, value); }
}
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
}
So when you declare an ImageButton in xaml, you can do something like this:
<local:ImageButton imageOn="/Resource/Path/To/imageOn.png" imageOff="/Resource/Path/To/imageOff.png" />

Related

One togglebutton controlling another togglebutton button

I have five toggle buttons
When the first toggle button is pressed, all remaining toggle buttons should be checked
The first toggle button xaml wrote:
<ToggleButton VerticalAlignment="Center" Width="230" Height="95" Command="{Binding Path=AllCommand}" CommandParameter="All" Style="{StaticResource ButtonStyle.AllButtonStyle}">
<TextBlock Text="full agreement" />
</ToggleButton>
The rest of the toglebutton xaml wrote:
<ToggleButton VerticalAlignment="Center" Width="66" Height="66" Command="{Binding Path=Agree1Command}" CommandParameter="Agree1" Style="{StaticResource ButtonStyle.check1ButtonStyle}" />
<Style x:Key="ButtonStyle.check1ButtonStyle" TargetType="ToggleButton">
<Setter Property="Background" Value="#d1d0cf" />
<Setter Property="Foreground" Value="#ffffff" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Button.Effect">
<Setter.Value>
<DropShadowEffect Color="Black" Direction="320" ShadowDepth="3" BlurRadius="5" Opacity="0.5" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsChecked" Value="False" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="#d1d0cf" />
</MultiTrigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Button.Background" Value="#332c27" />
<Setter Property="Button.Effect">
<Setter.Value>
<DropShadowEffect Color="Black" Direction="320" ShadowDepth="0" BlurRadius="0" Opacity="0" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
All togglebuton styles were applied equally as in the code
How can I check the other toggle button when I press the first toggle button?
be in desperate need of help
Slight modification to my above comment. Hopefully this works for you. You could try something like the following. Basically this creates a view model for both the list of toggle buttons and the toggle buttons themselves. The command for each toggle button is bound to the command on the datacontext for the ItemsControl, not the ItemsSource. The command then calls a service that invokes an event when the buttons are modified (eg; when they are all checked). The main viewmodel subscribes to this event so that it can pull the modified view models from the service.
View
<ItemsControl ItemsSource = "{binding Buttons}">
<ItemsControl.ItemTemplate>
<ToggleButton IsChecked = "{binding IsChecked}"
Text = "{binding ButtonText}"
IsChecked = "{binding Path=DataContext.ButtonClickedCommand}"/>
</ItemsControl.ItemTemplate>
</ItemsControl>
Button View Model
public class ButtonViewModel : ViewModelBase
{
private string buttonText;
public int ButtonText
{
get => buttonText;
set
{
myVar = value;
OnPropertyChanged(nameof(ButtonText));
}
}
private bool isChecked;
public bool IsChecked
{
get => isChecked;
set
{
isChecked = value;
OnPropertyChanged(nameof(IsChecked))
}
}
}
Main View Model
public class ListViewModel : ViewModelBase
{
public ObservableCollection<ButtonViewModel> Buttons { get; set; }
public ICommand ButtonClickedCommand;
private readonly ToggleButtonService buttonService;
public ListViewModel()
{
buttonService = new ToggleButtonClickedCommand(Buttons);
buttonService.ButtonChecked =+ UpdateButtons
ButtonClickedCommand = new ToggleButtonClickedCommand(buttonService);
}
private void UpdateButtons()
{
Buttons = buttonService.Buttons;
}
}
Toggle button Service
public class ToggleButtonService
{
private IEnumerable<ButtonViewModel> buttonViewModels;
public IEnumerable<ButtonViewModel> ButtonViewModels
{
get { return buttons; }
set
{
buttonViewModels = value;
ButtonsChecked?.Invoke();
}
}
public event Action ButtonChecked;
public ToggleButtonService(IEnumerable<ButtonViewModel> buttons)
{
buttonViewModels = buttons;
}
public void SelectOrUnSelectAllButtons()
{
List<ButtonViewModel> Buttons = new List<ButtonViewModel>(ButtonViewModels);
foreach(button in Buttons)
{
button.IsChecked = !button.IsChecked;
}
ButtonViewModels = Buttons;
}
}
ToggleButton Clicked Command
public class ToggleButtonClickedCommand : ICommand
{
private readonly ToggleButtonService ButtonService;
public ToggleBUttonClickedCommand(ToggleButtonService buttonService)
{
ButtonService = buttonService;
}
public event EventHandler? CanExecuteChanged;
public bool CanExecute(object? parameter)
{
return true;
}
public async void Execute(object? parameter)
{
ButonService.SelectOrUnSelectAllButtons();
}
}

WPF Custom control binding image source

I have a custom control based on a button, and I put an image inside. I can set the source of the image in the xaml, but if I try and bind it, it doesn't work.
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControl">
<Style TargetType="{x:Type local:MyCustomControl}" BasedOn = "{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid x:Name="InnerGrid">
<Image Source="pathname"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
This works just fine, however if I replace the <Image Source="pathname"/> with <Image Source={Binding MyImage, RelativeSource={RelativeSource Self}}"/>, and reference a Delegate Property in the class, it breaks.
MyCustomControl.cs
public class MyCustomControl : Button
{
static DependencyProperty m_myimage = null;
private DependencyProperty MyImageProperty
{
get
{
return m_myimage;
}
}
public BitmapImage MyImage
{
get
{
return (BitmapImage)GetValue(MyImageProperty);
}
set
{
SetValue(MyImageProperty, value);
}
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
MyImage = new BitmapImage(new Uri(pathname));
}
private static void RegisterDependencyProperties()
{
if (m_myimage == null)
{
m_myimage = DependencyProperty.Register("MyImage",
typeof(BitmapImage), typeof(MyCustomControl), null);
}
}
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl),
new FrameworkPropertyMetadata(typeof(MyCustomControl)));
RegisterDependencyProperties();
}
}
How can I get it to work?
Figured it out myself after about 2 hours. The xaml binding should look like,
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid x:Name="InnerGrid">
<Image Source="{Binding MyImage, RelativeSource={RelativeSource TemplatedParent}}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
Note that the binding relative resource went from RelativeSource={RelativeSource Self} to RelativeSource={RelativeSource TemplatedParent}

wpf validation rules are not kicking in

I have a Xaml page with style property for a text box. Whenever a user clicks the button with the textbox empty I need to display the validation error. Right now even when the textbox is empty and the button is pressed, a messagebox with empty text pops up. Even the Applicatin Exception is not working.. Please help...
Xaml:
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right"
Foreground="Orange"
FontSize="12pt">
!!!!
</TextBlock>
<Border BorderBrush="Green" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
My ViewModel Class:
public class Customer
{
private string _Text;
public string text
{
get
{
return _Text;
}
set
{
_Text = value;
if (string.IsNullOrEmpty(value))
{
throw new ApplicationException("Name is Mandatory");
}
}
}
public Customer()
{
}
private RelayCommand<object> _commandOkInstance;
public ICommand CommandOk
{
get
{
if (_commandOkInstance == null)
_commandOkInstance = new RelayCommand<object>(OkCommand);
return _commandOkInstance;
}
}
public void OkCommand(object obj)
{
MessageBox.Show(_Text);
}
}
Main Window.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Customer c = new Customer();
this.DataContext = c;
}
}

WPF Dependency Property In Nested User Control

I have the following WPF Control which is used to Show A Text With An Image Beside it
XAML Code
<UserControl x:Class="WFWorkSpaceWPF.UserControls.StackedImageTextCtl"
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"
Name="StackedImageText"
>
<Grid>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ElementName=StackedImageText, Path=ImageSource}" />
<TextBlock Text="{Binding ElementName=StackedImageText, Path=Text}" />
</StackPanel>
</Grid>
CS
public partial class StackedImageTextCtl : UserControl
{
public StackedImageTextCtl()
{
InitializeComponent();
}
#region "Properties"
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StackedImageTextCtl), new UIPropertyMetadata(""));
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(StackedImageTextCtl), new UIPropertyMetadata(null));
#endregion
}
In my project i want to reuse this control in other three user controls where it will be added as a part of these controls, As you see that the StackedImageTextCtl exposes Two Properties (Text and Image Source) that the parent User Controls needs to provide to it and these three controls will take the value from the container window throw XAML, I know that One of the ways to do so is to replicate defining the properties in each of these three user control and Using AddOwner functionality, but i am looking for a more better approach that won't require any repeat in code, can anyone please direct me to such way?.
This is your UserControl:
<UserControl ...>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}" Width="16"/>
<TextBlock Text="{Binding Text}" />
</StackPanel>
</UserControl>
and its code:
in addition to Text and ImageSource, there is State which represents the Add/Edit/delete state of this control, and there is StackCtlState which is an attached property and when attached to a FrameworkElement it represents the Add/Edit/delete state of that control.
public StackedImageTextCtl()
{
InitializeComponent();
DataContext = this;
}
//Text Dependency Property
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StackedImageTextCtl), new UIPropertyMetadata(null));
//ImageSource Dependency Property
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(StackedImageTextCtl), new UIPropertyMetadata(null));
//State Dependency Property
public AddEditDelete State
{
get { return (AddEditDelete)GetValue(StateProperty); }
set { SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State", typeof(AddEditDelete), typeof(StackedImageTextCtl), new UIPropertyMetadata(AddEditDelete.Add));
public static AddEditDelete GetStackCtlState(DependencyObject obj)
{
return (AddEditDelete)obj.GetValue(StackCtlStateProperty);
}
public static void SetStackCtlState(DependencyObject obj, AddEditDelete value)
{
obj.SetValue(StackCtlStateProperty, value);
}
public static readonly DependencyProperty StackCtlStateProperty =
DependencyProperty.RegisterAttached("StackCtlState", typeof(AddEditDelete), typeof(StackedImageTextCtl), new UIPropertyMetadata(AddEditDelete.Add));
I also define an enum:
public enum AddEditDelete { Add, Edit, Delete }
In Window xaml:
Each Button or ToggleButton have their attached property StackCtlState set to the desired value and also have their style set to one of styles for Button or ToggleButton.
Then these styles add a StackedImageTextCtl to the contents of styled button/togglebutton in the proper way that resources can be reused. (if you set just Content and don't set Template instead it will only show the contents of the last Button or last ToggleButton) The added StackedImageTextCtl has a State equal to the attached value of its TemplatedParent which is Button or ToggleButton.
At last the style uses Trigger to set the values for Text and Image, based on State of the StackedImageTextCtl.
<Window...
xmlns:myNamespace="clr-namespace:WpfApplication1">
<Window.Resources>
<Style TargetType="{x:Type myNamespace:StackedImageTextCtl}">
<Style.Triggers>
<Trigger Property="State" Value="Add">
<Setter Property="Text" Value="ADD"/>
<Setter Property="ImageSource" Value="/blue_1.jpg"/>
</Trigger>
<Trigger Property="State" Value="Edit">
<Setter Property="Text" Value="EDIT"/>
<Setter Property="ImageSource" Value="/blue_2.jpg"/>
</Trigger>
<Trigger Property="State" Value="Delete">
<Setter Property="Text" Value="DELETE"/>
<Setter Property="ImageSource" Value="/blue_3.jpg"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="stackButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Button>
<myNamespace:StackedImageTextCtl State="{TemplateBinding myNamespace:StackedImageTextCtl.StackCtlState}"/>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="stackToggleButtonStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ToggleButton>
<myNamespace:StackedImageTextCtl State="{TemplateBinding myNamespace:StackedImageTextCtl.StackCtlState}"/>
</ToggleButton>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<Button Style="{StaticResource stackButtonStyle}" myNamespace:StackedImageTextCtl.StackCtlState="Add"/>
<Button Style="{StaticResource stackButtonStyle}" myNamespace:StackedImageTextCtl.StackCtlState="Edit"/>
<ToggleButton Style="{StaticResource stackToggleButtonStyle}" myNamespace:StackedImageTextCtl.StackCtlState="Delete"/>
</StackPanel>

Optional Visibiltiy for Button in ControlTemplate

I have the following style for a Tabitem which contains a close button.
<Style x:Key="StudioTabItem" TargetType="{x:Type TabItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
...
<Button Grid.Column="2"
Width="15"
Height="15"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility={Binding}>
...
I would like to make the visibility of the StudioTabItems button optional when I use the actual control. So something like
<TabControl x:Name="tabControl"
Style="{StaticResource StudioTabControl}"
ItemsSource="{Binding Workspaces}"
SelectedIndex="{Binding SelectedIndex}"
TabStripPlacement="Top" >
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem"
BasedOn="{StaticResource StudioTabItem}"
IsCloseButtonVisible="False"> <-- How to do this?
See the IsCloseButtonVisible on the last line of the above. I know this is likely to involve DependencyProperties. Is this possible and how can I achieve this?
Thanks for your time.
This can be achieved by creating the Attached Property like below and by setting its property in style setter
public static class TabItemBehaviour
{
public static readonly DependencyProperty IsCloseButtonVisibleProperty =
DependencyProperty.RegisterAttached("IsCloseButtonVisible", typeof(bool), typeof(TabItemBehaviour), new UIPropertyMetadata(true, IsButtonVisiblePropertyChanged));
public static bool GetIsCloseButtonVisible(DependencyObject obj)
{
return (bool)obj.GetValue(IsCloseButtonVisibleProperty);
}
public static void SetIsCloseButtonVisible(DependencyObject obj, bool value)
{
obj.SetValue(IsCloseButtonVisibleProperty, value);
}
public static void IsButtonVisiblePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
TabItem item = o as TabItem;
if (item != null)
{
Button closeButton = item.Template.FindName("CloseButton", item) as Button;
if ((bool)e.NewValue == true)
{
closeButton.Visibility = Visibility.Visible;
}
else
{
closeButton.Visibility = Visibility.Collapsed;
}
}
}
}
And then TabItem style just set the property:
<Style TargetType="TabItem"
BasedOn="{StaticResource StudioTabItem}"
>
<Setter Property="behaviours:TabItemBehaviour.IsCloseButtonVisible" Value="False"/>
Also you will have to give Button a Name "CloseButton" in your ControlTemplate

Categories

Resources