WPF Events and references to objects, and how to manage them - wpf

I have a small WPF application, which has a Canvas and a Grid. I placed some custom user controls on the Grid. Now I would like to capture some mouse events on them. As a consequence of one event I would like to add (or modify) something to the canvas. However in the user control, you don't have a reference to the underlying canvas. First question, is there a way to get this reference, for example like getElementById(..) in JavaScript.
Also I know that you should avoid such references, if you want a clean architecture. In this case, whats a good practice to catch events at a specific user control and then to be able to invoke something on another object.

You do have access to the Canvas, Grid or any other element in your UserControl. The easiest way yo access them is to make sure each one has a name which is done by using the x:Name attribute.
<Grid x:Name="myGrid">
Then within your UserControl you can access it with myGrid. To access a Grid from outside the UserControl you would need to create a method in your UserControl that allowed you to manipulate it.
There is a this.FindName method you can use in a UserControl which is the equivalent of javascript's getElementById but you shouldn't need to use it given you can access objects directly with their name.

WPF has a new event architecture that may help you out here. So called "routed" events may either "tunnel" from the logical root container, through all intermediate containers, to the event source element, or "bubble" from the source element up (i.e. "tunneling" and "bubbling" events propagate in opposite directions).
All that to say that you can typically intercept events from child elements by registering an event handler at the container. Here's an example of intercepting button click events from buttons in a StackPanel:
<StackPanel ButtonBase.Click="HandleButtonClick">
<Button>Foo</Button>
<Button>Bar</Button>
</StackPanel>
And HandleButtonClick might be implemented like this:
private void HandleButtonClick(object sender, RoutedEventArgs e)
{
var button = e.OriginalSource as Button;
if (button != null) MessageBox.Show(button.Content.ToString());
}
Depending on what sort of "custom controls" you are using, this may not be possible. This is because not all events are "routed" events. WPF control events are usually routed events.

Related

Custom event for wpf UIElement

Is is possible to fire event when a UIElement's location is changed in wpf? We can fire location changed event in case of Windows but can we have a custom event which fires when a UIElement's location is changed in the Window.
It's not possible for the general case. The UIElement doesn't even know the location it's rendered to.
You can do it for particular cases, though. You can use events of the layout parent, like the Left and Top attached property of the Canvas, the scroll offset of a ScrollViewer, etc., depending on where your UIElement is in the visual tree.
Maybe this link will help you, UIElement supports a lot of Events. I think what you are looking for are Manipulation Events or the LayoutUpdate Event.
MSDN UIElement Class

WPF objects removed from Canvas hang arround responding to events

I've got a flowchart-style application drawn on a WPF Canvas. (Basically boxes connected with lines). The boxes are hooked up to various events on the 'model'. I need to able to clear the Canvas, without destroying the model.
What I do is remove all the boxes etc from the Canvas' Children collection. This works fine visually, but the controls seems to live on in limbo somewhere in memory. When I later fire events on the model, the controls crash my application. (the now-invisible boxes 'expect' to be the Child of a Canvas).
How should I indicate the controls are no longer wanted? I want them destroyed, not responding to events and bindings. Are the bindings to the model preventing WPF disposing of them? Would setting their Datacontext to null help?
Cheers, Jeff.
Sounds like you may need to have the boxes implement IDisposable and call Dispose on the boxes as you are removing them. In the dispose method you should unhook your event listeners. Or if you intend to boxes to be used again you may need to just expose some method to RemoveListeners() and then a method to AttachListeners() when you want it back in view.
public void Dispose()
{
model.MyEvent -= LocalListener;
}

creating a view within a custom control to hide parts

I'm creating a custom Silverlight control for Silverlight (specifically wp7). My control is mostly a button ,and when someone taps the button , I want to animate a tall rectangle filled with other contents that flys out from behind the button.
The issue is I do not know how to hide or only draw parts of this rectangle as I comes out. For example, when this rectangle is half way out,only the top half is showing while the rest is hidden. How can I do this without having to write some complex code? Presumably I just need to be able to define some sort of 'view' where only things inside this view are rendered to the screen.
Thoughts? Any help is appreciated!
You can use VisualStateManager to define possible visual states for your view. In addition, you can define transitions between those states (incuding animations). In your button's Click event handler you'd need to tell the VisualStateManager to transit to a different state - and you're done.
http://msdn.microsoft.com/en-us/library/system.windows.visualstatemanager.gotostate%28v=vs.95%29.aspx
This would probably look like this in your view's code behind :
void OnClick(object sender, RoutedEventArgs e)
{
VisualStateManager.GoToState(this, "StateName", true);
}
In order to easily define the animations, you can use Expression Blend 4
In order to ensure that only the parts of the animating rectangle that are within the parent container are shown and nothing outside of that, you need to be able to clip the parent's children to it's bounds. Unfortunately, there is no ClipToBounds property in Silverlight, but you can use Colin Eberhardt's clipping attached behavior to achieve the same effect.

A window that behaves both modally and non-modally

I want to create a WPF window that behaves as a modal dialogue box while at the same time facilitating selected operations on certain other windows of the same application. An example of this behaviour can be seen in Adobe Photoshop, which offers several dialogues that allow the user to use an eyedropper tool to make selections from an image while disabling virtually all other application features.
I'm guessing that the way forward is to create a non-modal, always-on-top dialogue and programmatically disable those application features that are not applicable to the dialogue. Is there an easy way to achieve this in WPF? Or perhaps there's a design pattern I could adopt.
Yes, there is the traditional approach you describe where you programmatically enable/disable features, but WPF also opens up several new possiblities that were really not possible in WinForms and older technologies.
I will explain four WPF-specific ways to do this:
You can secretly and automatically replace a window's contents with a picture of its contents using a Rectangle with a VisualBrush, thereby effectively disabling it. To the user it will look as if the window is unchanged, but the actual contents will be there underneath the picture, so you can use it for hit-testing and even forward selected events to it.
You can add a MergedDictionary to your window's ResourceDictionary that causes all TextBoxes to become TextBlocks, all Buttons to become disabled, etc, except as explicitly overridden using custom attached properties. So instead of looping through all your UI selectively enabling/disabling, you simply add or remove an object from the MergedDictionaries collection.
You can use the InputManager to programmatically generate and process real mouse events in particular parts of a disabled window, disallowing any mouse events that don't hit-test to something "approved."
Use data binding and styles to enable/disable individual controls rather than iterating through them
Details on replacing window with picture of window
For this solution, iterate your app windows and replace each content with a Grid containing the original Content and a Rectangle, like this:
<Window ...>
<Grid>
<ContentPresenter x:Name="OriginalContent" />
<Rectangle>
<Rectangle.Fill>
<VisualBrush Visual="{Binding ElementName=OriginalContent}" />
</Rectangle.Fill>
</Rectangle>
</Grid>
</Window>
This can be done programmatically or by using a template on the Window, but my preference is to create a custom control and create the above structure using its template. If this is done, you can code your windows as simply this:
<Window ...>
<my:SelectiveDisabler>
<Grid x:Name="LayoutRoot"> ... </Grid> <!-- Original content -->
</my:SelectiveDisabler>
</Window>
By adding mouse event handlers to the Rectangle and calling VisualTreeHelper.HitTest on the ContentPresenter to determine what object was clicked in the original content. From this point you can choose to ignore the mouse event, forward it to the original content for processing, or in the case of an eyedropper control or an object selection feature, simply extract the desired objects/information.
Details on MergedDictionary approach
Obviously you can restyle your whole UI using a ResourceDictionary merged into your window's resources.
A naiive way to do this is to simply create implicit styles in the merged ResourceDictionary to make all TextBoxes appear as TextBlocks, all Buttons appear as Borders, etc. This does not work very well because any TextBox with its own style or ControlTemplate explicitly set may miss the updates. In addition, you may not get all objects as desired, and there is no way to easily remove the Commands or Click events from buttons because they are explicitly specified and the style won't override that.
A better way to work this is to have the styles in the merged ResourceDictionary set an attached property, then use code-behind in the PropertyChangedCallback to update the properties you really want to change. Your attached "ModalMode" property, if set to true, would save all the local values and bindings for a number of properties (Template, Command, Click, IsEnabled, etc) in a private DependencyProperty on the object, then overwrite these with standard values. For example a button's Command property would be set to null temporarily. When the attached "ModalMode" property goes false, all the original local values and bindings are copied back from the temporary storage and the temporary storage is cleared.
This method provides a convenient way to selectively enable/disable portions of your UI by simply adding another attached property "IgnoreModalMode". You can manually set this to True on any UIElements that you don't want the ModalMode changes to apply to. Your ModalMode PropertyChangedCallback then checks this and if is true, it does nothing.
Details on InputManager approach
If you capture the mouse, you can get mouse coordinates no matter where it is moved. Translate these to screen coordinates using CompositionTarget.TransformToDevice(), then use CompositionTarget.TransformFromDevice() on each candidate window. If the mouse coordinates are in bounds, hit-test the disabled window (this can still be done even when a window is disabled), and if you like the object the user clicked on, use InputManager.ProcesInput to cause the mouse event to be processed in the other window exactly as if it was not disabled.
Details on using data binding
You can use a styles to bind the IsEnabled property of Buttons, MenuItems, etc to a static value like this:
<Setter Property="IsEnabled" Value="{Binding NonModal, Source={x:Static local:ModalModeTracker.Instance}}" />
Now by default all items with these styles will automatically disable when your NonModal property goes false. However any individual control can override with IsEnabled="true" to stay enabled even in your modal mode. More complex bindings can be done with MultiBinding and EDF ExpressionBinding to set whatever rules you want.
None of these approaches require iterating through your visual interface, enabling and disabling functionality. Which of these you actually select is a matter of what functionality you actually want to provide during modal mode, and how the rest of your UI is designed.
In any case, WPF makes this much easier than it was in WinForms days. Don't you just love WPF's power?
What you're looking for is similar to a Multiple Document Interface. This isn't available by default in WPF but there are some efforts out there to support this, both free and commercial.
It will be up to you to identify the current state of the application and enable/disable UI elements in response to this.
I think an always-on-top windows that programmatically disables certain app features is the way to do this. It might be easier to keep a "white list" of features that can be enabled while this form is open, and then disable everything that isn't on the list (as opposed to trying to maintain a "black list" of everything that can't be enabled).
I believe the best approach to solve this is using the InputManager approach mentioned previously. This design pattern allows you to connect commands to your toolbar buttons/menu items etc and each will call a CanExecute handler you specify for your command. In this handler, you would set the command to not enable if your always-on-top non-modal window was open.
http://msdn.microsoft.com/en-us/library/ms752308.aspx

How can I get WPF binding to occur on controls which have not yet been shown?

I have a custom text box control which raises a routed event when its TEXT property changes. This text property is data bound to a property on our view-model object.
When I place this control on a TabControl page or Expander control, it appears as if data binding only occurs when the control becomes visible for the first time, therefore I never receive any of the routed events until I swap to the tab the control is on or expand the expander.
Is there any way I can force data binding to occur before the control is shown?
Sounds like you relying on the data binding to genreate the routed event is the wrong approach. Instead you need to have your Model or ViewModel generate an event when the text is modified and then you watch this event from an appropriate place in your View.
Not very likely. WPF is a fairly efficient framework and won't do any work that it doesn't absolutely have to. This includes scenarios like data binding. Why bother exercising a collection for a control that might not ever be shown?

Resources