Custom Button with images per state - wpf

I'm trying to create a custom usercontrol that acts like a button, but i want it to use different images for each state (Normal, Hover, Pressed). The user control contains an Image control to show the image. I want to change the Source of the Image control at-runtime, so when the OnMouseEnter event triggers, i would change my image source to the HoverChange (ImageSource) property.
So i tried to add 3 ImageSource properties (NormalState, HoverState and PressedState) to the usercontrol so i can change the images when needed. (Coming from WinForms) But the problem is that the Properties aren't set in code (WinForms behaviour), so i can't assign them to my image. But when i use the usercontrol in my program i can set them via the property panel, but i can't use them in code (they stay NULL).
Here is some (pseudo) code of what i'm trying to reach:
public partial class ThreeStateButton : UserControl
{
public enum ButtonState
{
Normal,
Hover,
Pressed
}
public ImageSource NormalState { get; set; }
public ImageSource HoverState { get; set; }
public ImageSource PressState { get; set; }
public ThreeStateButton()
{
InitializeComponent();
SetState(ButtonState.Normal);
}
public void SetState(ButtonState state)
{
switch (state)
{
case ButtonState.Normal:
imgButton.Source = NormalState;
break;
case ButtonState.Hover:
imgButton.Source = HoverState;
break;
case ButtonState.Pressed:
imgButton.Source = PressState;
break;
}
}
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
SetState(ButtonState.Hover);
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
SetState(ButtonState.Normal);
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
SetState(ButtonState.Pressed);
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
SetState(ButtonState.Hover);
}
}
The main problem is that the ImageSource properties aren't set after initialization (and yes there are set through the property panel in the editor), i know the way of working is a bit different in WPF but how could i get this working the way i try?
Thanks

I did it on a more WPF-style way.
I create 3 image source properties and for each of them i created a DependencyProperty. I also added 3 Image controls in my xaml code and binded those imagesource properties using their registered name as DependencyProperty to the imagesource property of my image controls. Then it's just a matter of hiding the images or setting them visible on the correct state.

Related

(WPF) How can I find ancestor about two ItemsControl in code behind?

I have a ItemsControl(A). ItemSourece is a Class "Account".It contains some controls and another ItemsControl(B).
ItemsControl(B) includes some CheckBox. It's ItemSourece is ObservableCollection included in Class "Account". CheckBox's Content is binding to Content. CheckBox's IsChecked is bindind to IsChecked.
Now when I click CheckBox, I want to get ID and User in Class "Account", but I don't know hot to get them.
I already try to use
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
CheckBox checkBox = sender as CheckBox;
var parentElement = (ContentPresenter)VisualTreeHelper.GetParent(checkBox);
}
but it still can't get parent.
Althouth runtime can show parentElement.VisualParent, but actually it is not work.
Please help me! Thanks!
#RickyChen - I see your problem. AuthorityCheckBox binds to your visual checkboxes. When you click on a checkbox, there isn't any link back to the AuthorityCheckBox.
What you can do is abuse the Tag property of your checkbox and place the AuthorityCheckBox reference there.
Adjust your your AuthorityCheckBox class so it contains public Account Parent that is assigned in the constructor. This way you can easily get the AuthorityCheckBox parent:
public class AuthorityCheckBox
{
public string Content { get; set; }
public bool IsChecked { get; set; }
public bool IsEnabled { get; set; }
public Account Parent { get; private set; }
public AuthorirtyCheckBox(Account parent)
{
this.Parent = parent;
}
}
The event handler then looks like this:
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
AuthorityCheckBox acb = (sender as FrameworkElement).Tag as AuthorityCheckBox;
Account parent = acb.Parent;
}

WPF listboxitem PreviewKeyDown

I have a listbox that is bound to a list of custom objects. I can get the listbox items to display correctly using the ListBox.ItemTemplate in xaml. The custom objects for the listbox are all of the same base class outlined below.
public class HomeViewMenuItem : UIElement
{
private Uri _uri;
private IRegionManager _manager;
public HomeViewMenuItem(string text, Uri uri, IRegionManager manager)
{
this.PreviewMouseDown += HomeViewMenuItem_PreviewMouseDown;
this.PreviewKeyDown += HomeViewMenuItem_PreviewKeyDown;
_manager = manager;
Text = text;
_uri = uri;
ClickCommand = new DelegateCommand(this.Click, this.CanClick);
}
void HomeViewMenuItem_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Enter)
{
e.Handled = true;
this.ClickCommand.Execute();
}
}
void HomeViewMenuItem_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
this.ClickCommand.Execute();
}
private void Click()
{
_manager.Regions[RegionNames.MainRegion].RequestNavigate(_uri);
}
private bool CanClick()
{
return true;
}
public DelegateCommand ClickCommand { get; set; }
public string Text { get; set; }
}
The problem I am having is the HomeViewMenuItem_PreviewKeyDown method is not getting called. I believe this is because the method is getting called on the ListBoxItem itself first and getting handled there. I was able to verify this by obtaining a reference to the ListBoxItem object through listBox.ItemContainerGenerator.ContainerFromIndex(0) after the ItemContainerGenerator status changes to ContainersGenerated and adding an event handler there. This event handler correctly fired. Normally this would be an ok solution on a small project but I plan on having more listboxes with the same sort of functionality and would like to have a simpler/better solution. Is there a way that I can get my base class previewkeydown method to work?
The only solution I could think of is to have the base class inherit from ListBoxItem instead of UIElement then get the ListBox to create my items instead of ListBoxItems. But I dont think that is really possible without creating my own ListBox implementation.
You seem to be somewhat confused. In WPF, we create data items and declare DataTemplates to define what those items should look like in the UI. Our data items do not extend UI classes. If you have to handle the PreviewKeyDown event, then attach a handler to the UI element in the DataTemplate instead:
<DataTemplate>
<Grid PreviewKeyDown="HomeViewMenuItem_PreviewKeyDown">
...
</Grid>
</DataTemplate>

How to bind to a custom property in a Silverlight Custom control

I've created a custom control with, amongst others, the following:
public partial class MyButton : UserControl
{
public bool Enabled
{
get { return (bool)GetValue(EnabledProperty); }
set {
SetValue(EnabledProperty, value);
SomeOtherStuff();
}
}
}
public static readonly DependencyProperty EnabledProperty =
DependencyProperty.Register("Enabled", typeof(bool), typeof(MyButton), new PropertyMetadata(true));
public static void SetEnabled(DependencyObject obj, bool value)
{
obj.SetValue(EnabledProperty, value);
}
public static bool GetEnabled(DependencyObject obj)
{
return (bool) obj.GetValue(EnabledProperty);
}
}
In my XAML, I (try to) use binding to set the Enabled property:
<MyButton x:Name="myButtom1" Enabled="{Binding CanEnableButton}"/>
I know the bind between my control and the underlying data model is valid and working as I can bind 'IsEnabled' (a native property of the underlying UserControl) and it works as expected. However, my Enabled property is never set via the above binding. I've put breakpoints on my property set/get and they never get hit at all.
I can only imaging I've missed something relating to binding in my custom control. Can anyone see what?
I've tried implementing INotifyPropertyChanged on my control (and calling the PropertyChanged event from my Enabled setter) ... but that didn't fix it.
[ BTW: In case you are wondering "Why?": I can't intercept changes to the IsEnabled state of the base control, so I decided to implement and use my own version of a Enable/disable property (which I called Enabled) - one where I could plug my own code into the property setter ]
First of all drop the SetEnabled and GetEnabled pair, these only make sense for an attached property which is not what you are doing.
Now your main problem is that you are under the false assumption that the get/set members of your propery get called during binding, they don't.
What you need is to pass a call back method in the property meta data, it's here that you intercept changes and take other actions like so:-
public bool IsEnabled
{
get { return (bool)GetValue(IsEnabledProperty); }
set { SetValue(IsEnabledProperty, value); }
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.Register(
"IsEnabled",
typeof(bool),
typeof(MyButton),
new PropertyMetadata(true, OnIsEnabledPropertyChanged));
private static void OnIsEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyButton source = d as MyButton;
source.SomeOtherStuff();
}
private void SomeOtherStuff()
{
// Your other stuff here
}
With this in place regardless of how the propery is changed the SomeOtherStuff procedure will execute.
I'd suggest using the IsEnabledChanged event which is part of every Control/UserControl.
That would allow you to hook up to the event and do whatever actions you want to take.
public MainPage()
{
InitializeComponent();
this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(MainPage_IsEnabledChanged);
}
void MainPage_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Do SomeStuff
}

WPF, getting two way binding to work on custom control

Two way binding does not work on my custom control with the following internals:
public partial class ColorInputControl
{
public ColorInputControl()
{
InitializeComponent();
colorPicker.AddHandler(ColorPicker.SelectedColorChangedEvent,
new RoutedPropertyChangedEventHandler<Color>( SelectedColorChanged));;
colorPicker.AddHandler(ColorPicker.CancelEvent,
new RoutedPropertyChangedEventHandler<Color>(OnCancel));
}
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register
("SelectedColor", typeof(Color), typeof(ColorInputControl),
new PropertyMetadata(Colors.Transparent, null));
public Color SelectedColor
{
get
{
return (Color)GetValue(SelectedColorProperty);
//return colorPicker.SelectedColor;
}
set
{
SetValue(SelectedColorProperty, value);
colorPicker.SelectedColor = value;
}
}
private void SelectedColorChanged(object sender, RoutedPropertyChangedEventArgs<Color> e)
{
SetValue(SelectedColorProperty, colorPicker.SelectedColor);
}
}
SelectedColor is being bound to a property that fires INotifyPropertyChanged event control when it changes. However, I cannot get two-way binding to work. Changes from the UI are pesisted to the data source. However, changes originating from the data source are not reflected on the UI.
What did I miss? TIA.
Never do any work (updating the color picker) in the SelectColor helpers. Those are convinence wrappers and are not guarranted to be called. (As you can see in your two way binding.) Add a PropertyChangedCallback to your SelectedColorProperty metadata. Do your work in there.

Create Dependency Properties for setting Custom EventHandlers in XAML

i want to do add custom event handlers to default framework elements using DependencyProperties.
Something like the following:
<Border custom:MyProps.HandleMyEvent="someHandler">...</Border>
Here is the code behind for the control that contains the Border element:
public class MyPage : Page{
public void someHandler(object sender, EventArgs e){
//do something
}
}
Here is rough sample of how i imagine the class that defines the property:
public class MyProps{
public event EventHandler MyInternalHandler;
public static readonly DependencyProperty HandleMyEventProperty = ...
public void SetHandleMyEvent(object sender, EventHandler e){
MyInternalHandler += e;
}
}
The problem is that I don't know/didn't find any hints how to combine DependencyProperties with events/delegates and EventHandlers.
Do you have a clue?
I'm going to assume this has nothing to do with WPF, this is a silverlight question.
First of all you can't simply add an Event to an existing control. After all you are adding attached Properties whereas events are handled differently, they're not properties.
You need to create a new type which has this event then create an attached property of this type.
Here is a basic type that simply has an event:-
public class MyEventer
{
public event EventHandler MyEvent;
// What would call this??
protected void OnMyEvent(EventArgs e)
{
if (MyEvent != null)
MyEvent(this, e);
}
}
Now we create an attached property which has MyEventer as its property, I prefer to place these in a separate static class.
public static class MyProps
{
public static MyEventer GetEventer(DependencyObject obj)
{
return (MyEventer)obj.GetValue(EventerProperty );
}
public static void SetEventer(DependencyObject obj, MyEventer value)
{
obj.SetValue(EventerProperty , value);
}
public static readonly DependencyProperty EventerProperty =
DepencencyProperty.RegisterAttached("Eventer", typeof(MyEventer), typeof(MyProps), null)
}
}
Now you attach this to a control like this:-
<Border ...>
<custom:MyProps.Eventer>
<custom:MyEventer MyEvent="someHandler" />
</custom:MyProps.Eventer>
</Border>
If you compile the project before writing this xaml you'll note that Visual Studio will offer you the option for it to create the event handler in the code behind for you.
Of course this still leaves a significant question: How did you intend to cause the event to fire?

Resources