I want to create a UserControl with several controls inside. These controls should behave simmilar to the radio buttons, i. e., the status of one of them affects the status of the rest. To do that I would like to be able to handle the OnClick event on each of the controls from the parent.
One solution could be to call a method of the parent, to perform the global UserControl change, from the child controls' OnClick method. Something like:
class Child : UserControl
{
...
protected override void OnClick(EventArgs z_args)
{
// do own stuff
((PartentType)Parent).ChangeStatus(this);
}
...
}
This is a solution, but I wonder if there is a more standard an elegant way to solve this issue. Thanks!
No, this is very bad, a control should never depend on having a specific parent. Do it the same way any Windows Forms control does it: if something interesting happens that a parent might be interested in then raise an event:
public event EventHandler StatusChanged;
public int Status {
get { ... }
}
protected override void OnClick(EventArgs z_args) {
// do own stuff
//...
var handler = StatusChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
Consider putting the event raising code in a private setter for the Status property.
Related
I've been reading up on delegates, event and WPF for some days now and I am starting to get some understanding of it but there are a few things that is unclear to me.
In this question they explained on how to raise an event on a property is changed.
From what I've understood when it comes to event is that you want to do something when they happend, and that you need to add an function to an event like so
Someclass.PropertyChanged += new PropertyEventHandler(somefunction)
public void somefunction(object sender, EventArgs e){ //Do some code}
But almost in every example, when they use INotifyPropertyChanged that is never used, but they somehow manage to activate the event PropertyChanged.
I can't really make sense of it.
Do you need to add function to a new eventhandler if you implement an interface with an already declared event?
No need to give it a handler. You implement PropertyChanged so some other code can handle the event. That other code might be yours, but in the case of INotifyPropertyChanged, it's usually the bindings in your views that'll subscribe to your PropertyChanged events.
You can declare an event without adding your own handler to it. You really ought to raise the event once you bothered declaring it, but you don't have to handle it. By raising it, I mean like this:
protected void OnPropertyChanged(String propName)
{
var handler = PropertyChanged;
// If nobody gave it a handler, it'll be null, so check for that.
if (handler != null)
{
// This is what we refer to when we say "raise the event": handler has
// references to at least one handler (because it's not null), and possibly
// dozens. This one "method call" here will magically call all of them.
handler(this, new PropertyChangedEventArgs(propName));
}
}
public String Name {
get { return _name; }
set {
if (_name != value) {
_name = value;
// Call this method to raise PropertyChanged
OnPropertyChanged("Name");
}
}
}
private String _name;
Declaring an event is just saying "In case anybody cares about this thing happening, here's an event that I'll raise when it happens."
Maybe you want to handle that event in some other part of your own code. In WPF, you implement INotifyPropertyChanged so when the user interface has bindings to the properties of an instance of your class, it'll get the notifications it needs.
I'm a web and backend programmer by nature. Normally I try to avaoid making windows programs. Now I have to make a WPF client.
I have a background task that raises an event every often time. (It is working like a poller and when the criteria are met an event is raised). Noob as I am I wrote this code that was attached to the event to update the UI.
private void IsDisconnectedEvent()
{
UserWindow.Visibility = Visibility.Hidden;
DisconnectWindow.Visibility = Visibility.Visible;
}
This gives an exception because I am not on the same thread. After some googling I found that I should change the code with:
private void IsDisconnectedEvent()
{
Dispatcher.Invoke(() =>
{
UserWindow.Visibility = Visibility.Hidden;
DisconnectWindow.Visibility = Visibility.Visible;
});
}
This works, but this is not the only event and thus makes my code horrible ugly. Are there better ways to do this?
Regarding this:
This works, but this is not the only event and thus makes my code
horrible ugly
Yes, your WPF-based code will definitely be extremely horrible unless you understand and embrace The WPF Mentality.
Basically, all interactions between your custom logic (AKA Business logic or Application Logic) and the WPF UI should manifest in the form of Declarative DataBinding as opposed to the traditional imperative approach.
This means that there should be nothing like this:
UserWindow.Visibility = Visibility.Hidden;
anywhere in your code, simply because introducing things like that makes your code dependent on the UI and thus only executable on the UI thread.
Instead, the WPF approach to that would be to declaratively DataBind the Visibility propety of the UI element (IN XAML) to a relevant bool property that you can operate from the outside, like this:
<UserWindow Visibility="{Binding ShowUserWindow, Converter={my:BoolToVisibilityConverter}}">
<!-- ... -->
</UserWindow>
Then, you would need to create a relevant class that contains the properties the UI is expecting to bind to. This is called a ViewModel.
Notice that in order to properly support Two-Way WPF DataBinding, your ViewModels must Implement the INotifyPropertyChanged interface.
When doing so, it is also convenient to have the PropertyChanged event from that interface marshalled to the UI thread, so that you no longer have to worry about setting the ViewModel's properties by using the Dispatcher.
Therefore our first step is to have all our ViewModels inherit from a class like this:
(taken from this answer):
public class PropertyChangedBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
//Raise the PropertyChanged event on the UI Thread, with the relevant propertyName parameter:
Application.Current.Dispatcher.BeginInvoke((Action) (() =>
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}));
}
}
Once we have our Property Change Notification Dispatch to the UI Thread in place, we can proceed to create a relevant ViewModel that suits, in this case, the UserWindow and it's DataBinding expectations:
public class UserViewModel: PropertyChangedBase
{
private bool _showUserWindow;
public bool ShowUserWindow
{
get {return _showUserWindow; }
set
{
_showUserWindow = value;
OnPropertyChanged("ShowUserWindow"); //This is important!!!
}
}
}
Finally, you would need to set the Window's DataContext to an instance of it's corresponding ViewModel. One simple way to do that is in the Window's constructor:
public UserWindow() //Window's Constructor
{
InitializeComponent(); //this is required.
DataContext = new UserViewModel(); //here we set the DataContext
}
As you can see in this example, there is literally no need to manipulate the UI element's properties in procedural code. This is good not only because it resolves the Thread Affinity issues (because now you can set the ShowUserWindow property from any thread), but also because it makes your ViewModels and logic completely decoupled from the UI and thus testable and more scalable.
This same concept applies to EVERYTHING in WPF.
One detail that I need to mention is that I'm making use of a technique of Combining MarkupExtension and IValueConverter in order to reduce the the XAML boilerplate involved in using Converters.
You can read more about that in the link and also the MSDN DataBinding page linked above.
Let me know if you need further details.
When I first started writing WPF custom controls, if I wanted to add an event handler, I would do so in the control's OnApplyTemplate override, after getting the template part:
public void override OnApplyTemplate() {
if ( addMenu != null ) {
addMenu.Click -= addMenu_Click;
addMenu = null;
}
addMenu = (MenuItem)Template.FindName("PART_AddMenu", this);
addMenu.Click += addMenu_Click;
}
But then one day I noticed that OnApplyTemplate() is not always called when I'd expect it to be, i.e. when the control is disconnected from the visual tree. That is, using the above technique, the event handlers won't always be removed. So I came up with a different way:
public MyCustomControl()
{
Loaded += this_Loaded;
}
void this_Loaded(object sender, RoutedEventArgs e)
{
Unloaded += this_Unloaded;
addMenu = (MenuItem)Template.FindName("PART_AddMenu", this);
addMenu.Click += addMenu_Click;
}
void this_Unloaded(object sender, RoutedEventArgs e)
{
Unloaded -= this_Unloaded;
if (addMenu != null)
{
addMenu.Click -= addMenu_Click;
addMenu = null;
}
}
This way seems to do the trick. Does everyone concur that this is the better way of hooking up and removing event handlers in a custom control? If not, then why?
This method is fine, but you do have to understand that you get the unloaded event at times that you might not want the event handlers unhooked. For example, let's say you have a tab control. When you switch TabItems the content of the previous TabItem all gets Unloaded and then reloaded when the TabItem becomes selected again. This is fine for things like Button.Click because you can't perform such actions on an inactive tab, but any events that don't require the item to be loaded into the visual tree will be disconnected even though the items still exist.
Why do you feel you need to clean up all event handlers? I realize that there are some cases where they can hang onto a reference of another object, but this is an unusual case and is usually best handled by cleaning them up when used in that way. Here's some better details on this: How built-in WPF controls manage their event handlers to an attached event?
WPF controls (such as ComboBox) uses the OnTemplateChangedInternal() method to unregister events that are registered in OnApplyTemplate(). You can't override that method, as it's internal to the PresentationFramework dll, but you can override the protected OnTemplateChanged() method to do the same - it's called by OnTemplateChangedInternal() in the Control base class.
Here's sample code that could go into your custom control:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
EditableTextBoxSite = GetTemplateChild("PART_EditableTextBox") as TextBox;
EditableTextBoxSite.TextChanged += new TextChangedEventHandler(this.OnEditableTextBoxTextChanged);
this.EditableTextBoxSite.PreviewTextInput -= new TextCompositionEventHandler(this.OnEditableTextBoxPreviewTextInput);
}
protected override void OnTemplateChanged(ControlTemplate oldTemplate, ControlTemplate newTemplate)
{
base.OnTemplateChanged(oldTemplate, newTemplate);
if (this.EditableTextBoxSite == null)
return;
this.EditableTextBoxSite.TextChanged -= new TextChangedEventHandler(this.OnEditableTextBoxTextChanged);
this.EditableTextBoxSite.PreviewTextInput -= new TextCompositionEventHandler(this.OnEditableTextBoxPreviewTextInput);
}
I'm not sure about all the implications of doing this, but it does seem to be the closest way to emulate what WPF controls do.
I have an Inkcanvas in my project (myPaint)
What is the name of event for add or remove the children (UiElement) from InkCanvas. for example I want handle this event : myInkCanvas.Children.remove(myRectangle) or this example :
myInkCanvas.Children.Add(myRectangle)
There isn't an event you can listen to that is fired when elements are added to or removed from the Children collection. There is a virtual protected method that is called, which you could leverage, called OnVisualChildrenChanged.
This isn't directly tied to the Children collection, as elements can add/remove visuals separate from that. But for InkCanvas, it would probably be safe.
So you'd use something like:
public class MyInkCanvas : InkCanvas {
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved) {
// TODO: Raise event or do something
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
}
}
What exactly do you need this for? Can you use the StrokeCollected event instead?
I need to add an event handler to an inherited control like a datagridview but Visual Studio doesn't allow me. Isn't there a way for an inherited control to fire a base event handler AND the inherited one? in a sequence I specify?
Your question is unclear. Assuming that you're trying to handle the base class' event in the inherited control, you can override the OnEventName protected virtual method. In the override, make sure to call the base method or the event won't fire.
This method exists (AFAIK) for every event in every control in System.Windows.Forms. If the control you're inheriting does not have this virtual method, you can subscribe to the vent in the constructor.
For example:
class MyButton : Button {
//First way
protected override void OnClick(EventArgs e) {
base.OnClick(e); //Without this line, the event won't be fired
//...
}
//Second way
public MyButton() {
base.Click += Base_Click;
}
void Base_Click(object sender, EventArgs e) {
//...
}
}
EDIT:
If you're trying to raise the base class' event, you can call these OnEventName methods.
If the base class doesn't have one, the only way to do it is to declare a new event, and add a handler to the original event from the base class that raises your new event. Note that if you have other code that uses the base class' event, and the evnet is not virtual and doesn't have a raiser method, you're out of luck, unless you can decompile the base clas and find out where the event is raised.
Events in WinForms typically have a corresponding "On*" method. Simply call that method, and it'll raise the event. If you want to raise "CellClick" for example, call "base.OnCellClick(new DataGridViewCellEventArgs(row, column))".