I have an items control with custom panel. Based on this itemsControl I am writing an Behavior (Behavior)
What I need is whenever arrange override for my ItemsPanel is called my behaviour should get notified. In my behavior I have access to the itemsControl and the itemsPanel.
One way is I can raise an event from ItemsPanel override and subscribe it in my Behavior.
But if you there is a better solution please can you suggest?
Here is how I would try to do it:
Create a "fake" control
class ArrangeNotifier : Control
{
public event EventHandler OnArrange;
protected override ArrangeOverride(blabla)
{
(OnArrange??delegate{})(blabla);
}
}
On your "OnAttached" of your behavior, create a var myNotifier = new ArrangeNotifier() that you will add to the panel AttachedObject (which obliges your behavior to be a Behavior<Panel>)
Suscribe to myNotifier.OnArrange from your behavior, and do your stuff in the handler
Do not forget to remove your fake control in "OnDettached" of your behavior
[Edit] I misread your question, I guess that this was what you suggested :)
I dont see any another way.
Related
I have a custom control that has a refresh method, something similar to this:
public class MyControl : Canvas
{
// Dependency property for "data" used to draw the control here
public void Refresh()
{
Children.Clear();
// Using data, draw the control
Children.Add(new Line(...));
Children.Add(new Rectangle(...));
// etc.
}
}
Right now, I have to call Refresh() manually each time I want the look of the control to update. My dependency properties are set up for FrameworkPropertyMetadataOptions.AffectsArrange, so WPF knows that modifying the properties will affect the arrangement of the control and that it should be redrawn. So here's the question:
What does WPF use to tell a custom control that it should be redrawn? Is it an event, or an override, and how should it be used? I've tried handling various events and overrides and nothing seems to work. So, what's the correct way to do this? I want to replace / wrap the Refresh() method above in something "automatic" that WPF will handle automatically.
I have a User Control that contains a list of items and I raise an event when the currentIndex changes, also, when it changes, I must call two other methods two verify and change the appearance of the Control (change an Image and block/unblock some buttons).
What I want to know, mostly out of curiosity because it is already working, is when is it more appropriate to call these two methods?
Should I call them within the CurrentIndex property per se? Should I call them within the OnCurrentIndexChanged(...)? Should I handle the event within the class and do it there?
I'll assume you've implemented the standard event generating pattern and made OnCurrentIndexChanged protected virtual so that a derived class can override the method and alter the event generation and/or handling.
Unfortunately that requires reading tea leaves, why would anybody want to override the method? And more seriously, how could overriding the method break your control when they do? That's awfully hard to guess at for anybody that doesn't know the code well, not exactly easy for you either. The principle to apply here, used in the .NET framework code as well, is to do as little as possible. Just raise the event, nothing else. Which minimizes the odds of breakage when the derived class does something silly, but entirely common, like not calling base.OnCurrentIndexChanged.
The behavior of your controls is an implementation detail of your UserControl. So change their properties in your CurrentIndex property setter, then call OnCurrentIndexChanged(). Anybody that derives from your class can override that behavior, if necessary. And nothing goes wrong when they forget to call your OnCurrentIndexChanged() method. But do note that you need to make the control variables protected instead of private. So they can override the behavior, if they need to.
And don't hesitate to just not use a virtual method at all if this is too spooky for you. It's not common to have to accommodate hundreds of thousands of programmers with your controls :)
In the user control, I would have a property that represents the selected item. Then, during the setter of the object, raise the event method to change your user control. That way, in the future, if you need to add more listeners, you just need to add another handler in the setter method. This is pretty common in MVVM applications and is pretty maintainable.
Because your UserControl acts as a ListControl, you need to implement two events and two properties.
public event System.EventHandler SelectedIndexChanged;
public event System.EventHandler SelectionChangeCommitted;
public int SelectedIndex {
get;
set;
}
public T SelectedItem { // Where T is whatever your type is
get;
set;
}
SelectedIndexChanged should always be used for actions that always need to be triggered when your selected index is changed. SelectionChangeCommitted should only be triggered when the user physically changes the selection. The separation between the two is an important distinction, and most controls in .NET follow this pattern (eg. ComboBox), but may not use the same name for the events.
Now, with that said, if the controls you need to change properties for are also within the same user control, then you should of course handle that within the user control code in the appropriate event. Otherwise, the code should be orphaned to whoever implements the user control (eg. a form or another user control) by subscribing to the event and doing the work there.
The order really depends on your requirements, but SelectedIndexChanged should always be raised (but not more than once per change as that would introduce strange behavior), and again SelectionChangeCommitted should only be raised by the user (eg. setting SelectedIndex or SelectedItem).
A good rule of thumb is if your internal stuff MUST happen before the user knows about it, call SelectedIndexChanged first, then SelectionChangeCommitted. If it doesn't matter, either or. Changing the order later on could result in breaking changes in whoever implements the control, so make sure your decision is solid.
The difference between the two is SelectedIndex and SelectedItem would be updated by things like clearing your list internally, adding new items, et cetera, but does not necessarily mean it was a physical user action that should result in both your events firing.
Hope this helps.
I would like to create a custom version of the TabControl so that when a new TabPage is added I can ensure some custom processing is performed.
The question is how do I override the TabPages.Add() method to achieve this?
thanks,
Richard
Unfortunatelly, you cannot override Add() method of TabPageCollection class. What you may try is to subscribe to TabControl.ControlAdded event in hope that it will be raised when a TabPage (which is essentially a Control as well) will be added.
You could create the custom version which inherits from the TabControl, and has a public new void Add(string) method. But if anyone casts your control back to the TabControl, they would go around your logic. You could try creating a custom control which inherits from System.Windows.Forms.Control and expose all the methods of a private TabControl, modifying the Add method as needed. This would give you much more control.
i need to override the base CheckedListBox behaviour.
it is possible to check and uncheck a CheckedListBox without any code attached to it.
i need to disable this behaviour so that i can implement custom code.
any ideas?
thanks.
.
for example:
if (ListenCheckedListBox.GetItemChecked(0))
{
ListenCheckedListBox.SetItemChecked(0, false);
}
if (!ListenCheckedListBox.GetItemChecked(0))
{
ListenCheckedListBox.SetItemChecked(0, true);
}
does not work because the controls default behaviour already does this anyway.
hopefully you can understand my issue now.
You can create your own CheckedListBox by inheriting from the built-in class and overriding the relevant methods.
As I understand your question, you don't want the items to be selected when the user clicks them, you want to control the selection entirely from your code.
To do this, you can override the OnItemCheck method, and control the new value that is being set:
public class CheckedListBoxEx : CheckedListBox
{
protected override void OnItemCheck(ItemCheckEventArgs ice)
{
ice.NewValue = ice.CurrentValue;
}
}
This can also be done by simply handling the ItemCheck event.
ok i figured it out. i was seeing it wrong. i just handle for the checked state instead of defining it twice. once by the control second by me.
yes i was being silly!
I have a WinForms ListView, obviously containing ListViewItems. I'd like to be able to attach a click event to each item, instead of to the entire ListView (and then trying to figure out what item was clicked). The reason for this is that I need to perform a different action based on which item was selected. The ListViewItem class seems to be very limited in this regard. Is there any way to do what I want, or am I forced to use the ListView.Click event?
I would still use the ListView Click event.
A trick I've used in these situations is to use the Tag property of a ListViewItem. It's great for storing per item data and you can put anything in it.
It may make sense to subclass ListViewItem and use virtual dispatch to select the appropriate behavior based on the selected ListViewItem in the appropriate ListView event.
E.g. (uncompiled)
public abstract class MyItems : ListViewItem
{
public abstract DoOperation();
}
public class MyItemA : MyItems
{
public override DoOperation()
{ /* whatever a */ }
}
public class MyItemB : MyItems
{
public override DoOperation()
{ /* whatever b */ }
}
// in ListView event
MyItems item = (MyItems)this.SelectedItem;
item.DoOperation();
As others have mentioned, it may also make sense to use the appropriate Tag property. Which technique you go for really depends on what your action is (and therefore where it belongs, architecturally). I assumed the subclass made more sense because you're looking for a click on a listview item, and that (to me) seems more likely to be presentation-layer b/c you're overriding some standard control behavior (which would normally just select an item) as opposed to doing something in response to behavior.
In most use cases, a ListViewItem is a representation in the UI of some object, and what you're trying to do is execute a method of the object that the ListViewItem represents when the user clicks on it. For the sake of simplicity and maintainability, you want as few things to sit between the user's mouse-click and the actual method being executed.
You can store the object in the ListViewItem's Tag property and then reference it in the Click event handler, but that results in code that's got some inherent weak points:
private void MyListView_Click(object sender, EventArgs e)
{
ListView l = (ListView)sender;
if (l.SelectedItem != null)
{
MyClass obj = l.SelectedItem.Tag as MyClass;
if (obj != null)
{
obj.Method();
}
}
}
That's a lot of casting and null-reference checking. And the really weak thing about this code is that if it turns out that Tag is null, or contains something other than a MyClass object, you don't really know where to look to find out where the problem is occurring.
Contrast it with code like this:
private void MyListView_Click(object sender, EventArgs e)
{
MyClass.ListViewClicked(sender as ListView);
}
When you're maintaining this code, you don't know how that ListViewClicked method is implemented, but at least you know where to look for it - in MyClass. And when you do, you'll see something like this:
public static void ListViewClicked(ListView listView)
{
if (listView.SelectedItem == null)
{
return;
}
if (ListViewItemLookup.ContainsKey(listView.SelectedItem))
{
ListViewItemLookup[listView.SelectedItem].Execute();
}
}
Well, that's interesting. Following the thread, how does that dictionary get populated? You find that in another method in MyClass:
private static Dictionary<ListViewItem, MyClass> ListViewItemLookup =
new Dictionary<ListViewItem, MyClass>();
public ListViewItem GetListViewItem()
{
ListViewItem item = new ListViewItem();
item.Text = SomeProperty;
// population of other ListViewItem columns goes here
ListViewItemLookup.Add(item, this);
return item;
}
(Reasonable people can disagree about whether or not it's appropriate for a class to be so closely tied to a specific form of its representation in the UI - there are those who would isolate these methods and this dictionary in a helper class instead of in MyClass itself, and depending on how hairy the rest of the problem is I might do it too.)
This approach solves a number of problems: it gives you a simple way of handling the ListView's Click event properly, which is what you asked for. But it also isolates the not-always-trivial process of creating the ListViewItem in the first place. It reduces the amount of code you'll have to move around if you refactor your form and move the ListView to another form. And it reduces the number of things that your form class needs to know about, which is generally a good thing.
Also, it's testable. Generally, the only way to test code in a UI event handler is through the UI. This approach lets you isolate all of the logic surrounding this part of the UI in something that you can unit test; the only thing you can't write a unit test for is a single line of code in the form.
I should point out that the other approach people have been suggesting - subclassing ListViewItem - is perfectly fine too. You put the logic I put in the GetListViewItem method in the class's constructor, make the MyClass instance a private property of the class, and expose a Click method that calls the method of MyClass. Pretty much the only reason I don't like it is that it still leaves you with a fair amount of code in your form that you can't really unit test:
ListView l = (ListView)sender;
if (l.SelectedItem != null)
{
MyClassListViewItem item = l.SelectedItem as MyClassListViewItem;
if (item != null)
{
item.MyClass.Method();
}
}
You might however have luck sticking a reference to a delegate or other handler in the tag field (assuming there is a tag property of a ListViewItem). You would still have to determine which ListViewItem is clicked, but you could then go straight to the tag instead of another decision structure.
You want to create a new class (or classes if there are various types), which inherits from ListViewItem, then populate your ListView with these objects (as long as they inherit from listview (even several levels of inheritence) The ListView control will take them).
Then add a click method to your custom class(es) and on the ItemClick event of your listView, just call the click method of the clicked item. (some casting may be needed)
Actually there is no way to use a ListViewItem. You have to use the ListView itself. By using the 'SelectedItems' property of the ListView you can access the selected ListViewItems.
One option is to override the ListViewItem class an implement the specific stuff in there. Then you can cast the selected item to the overridden one and perform the action.
I really don't understand the reason to do so instead of just using the regular ListView Click event, but if I were to do like you suggest I would assign an EventHandler delegate to the Tag property of each ListViewItem, then in the ListView Click event handler I would check if the ListViewItem.Tag <> null, and if so call the delegate.