A window that behaves both modally and non-modally - wpf

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

Related

How to show different controls based on selection(s)

I have a user control that has a list of items.
If an item is selected, I want to show/replace the view with a panel and some buttons and what not.
When the user has finished with this view by pressing a button or something, I want to revert back to the list again and continue the process.
Essentially, this is a wizard.
What to do?
Sorry, I forgot to mention that I am using MVVM.
Solution:
Thanks to all for the help. My test application wasn't working which prompted me to ask SO.
My test application wasn't working because I had missed the {x:type} in the DataTemplate.
To simply:
I created different datatemplates in the resources with the {x:Type}
My viewmodel:
contained a compositecollection of IWizardPageViewModel.
contained a currentPage property.
contained NextCommand/BackCommand to change the currentPage
I bind the currentPage property to the control and the datatemplates take over.
Because of the {x:Type} it wasn't working.
I don't know whether this is right or wrong, but it works and is mostly controlled by the viewmodel rather than triggers on the view.
Consider a list of usercontrols - one for each page of your wizard. The top level usercontrol (the wizard control) will own this list. For navigation, you can;
Have buttons on the top level wizard usercontrol. When pressed, these notify the children of the navigation so that they can finish their work or cancel the navigation. You will want a common interface for the pages. IWizardPage perhaps?
Use a routed commands to notify the wizard usercontrol http://msdn.microsoft.com/en-us/library/ms752308.aspx#Four_main_Concepts
You can use Triggers and Selectors to update the view (DataTemplate) based on user actions.
Let me know if you need code snippet for the same.
First thing that comes to my mind (and easiest) is to use Visibility property and bind it to some boolean flags in ViewModel that will indicate current UI state. Of course in this case you should apply a Converter to properly convert bool value to Visibility. There are dozens of examples of such kind of convertors.
But this is relevant only in for of small amount of such Controls. In case of really lots of UI elements that should be shown and replaced on a view it's better to use framework like Prism. From scratch it will be not so straightforward, but then you'll feel all power of flexibility.
In case you're not following MVVM culture (or you don't like having such backing properties) you can bind Visibility property of control A that should be shown to Booleaen property of element B which stands for show/hide logic. To make it clear:
<TextBlock x:Name="A" Visibility="{Binding IsChecked, ElementName=B, Converter={StaticResource boolToVisibilityConverter}}" Text="Some text."/>
<ToggleButton x:Name="B" IsChecked="False"/>

Subclassing WPF control themes, why not only PARTIAL subclass

So much reading, and so much about inheritance, I can't find any direct answers, so here goes.
If you have a base-class derived to do certain things, look or act a certain way, you can subclass it and get all the functionality of the parent class with only slightly modified differential. The same does not appear to be the same for working with WPF Themes... more specifically, the combobox control (similar issues with textbox, but that's obviously less complex).
By looking at the Control Template Examples, they discuss the entire structure of it, the borders, backgrounds, glyphs, actions, properties, etc.
If the ONLY thing I want to do with a combobox is to change the border of it to Red if there is an error in it, it appears, I have to basically redefine the entire thing and somehow put in my custom trigger setting / color to be implemented.
Somewhat similar is that of the textbox control and how its created. It has the named control when trying to nuts around with the background color... you can't just say... background = some static brush value.
What shortcuts are out there to only allow overriding these small elements without having to re-create the entire template control. I can just imagine what would go on with grids, tabbed controls, and others that could get extremely messed up if you miss one simple thing.
I also see that some controls are made up of OTHER Control.Templates, so how might I be able to attach to changing the property setting on just the single element of the control template... Such as the combobox has the control template for the Toggle Button. From that, it has a border via x:Name="Border" and I want to change THAT element within a derived style.
Thanks
I might not understand your question here. But from what i get is:
Yes you can't partially implement Templates, in fact i wouldn't know how this could be possible. But, if you want to change certain things, you can of course do that. You can create Styles, Templates, Brushes etc. as DependencyProperties and use TemplateBinding to bind to them, on the given child control.
Remember that WPF allows always to change the template on the fly. if we could partially change the template this would might hurt performance or could get messy and complicated. Still, you can do that using ContentControls and TemplateBinding or simply Triggers.
For my custom controls, which might contain multiple part sub controls, i usually add a style for them. For example, a custom ComboBox would contain a ToggleButtonStyle.
One thing that would be nice though, would be to add control template triggers without the need to reimplement the template.

Lightbox-style popup in WPF -- how?

(I am trying to learn WPF using tutorials and documentation, and trying to develop a user interface for my backend-complete application while I do say. I've heard people say that the learning curve is quite steep. But sometimes I wonder whether what I'm trying to do is actually something that's hard to do in WPF, or if it's simple but I'm thinking in wrong terms, or if it's neither, it's quite simple but I just happen not to know how.)
Here's my current question. I wanted clicking that clicking some part of my UI will bring up a 'popup' where the user can enter more information. I would like a 'lightbox-style' popup, i.e. the popup is modal to the page, it darkens the rest of the page to become the center of attention, etc. These are seen commonly on Web sites.
A bit of searching led me to the WPF Popup control. I added it, put my content in, set the IsOpen property to True, and -- presto! A popup. Then I added an invisible Rectangle that covers my whole window, and set it to Visible as well when I want my popup to open. Great!
So now I wanted to do this dynamically, because sometimes I will be loading a record which will sometimes have a need to open another control (a UserControl) in a popup to edit its information. So I made myself a method called OpenPopup. But I can't seem to find a way to write this method using WPF. In Windows Forms I'd have written: (I use VB.NET)
Sub ShowPopup (form as Form, ctrl as Control)
'Create 'rect' as new dark rectangle control
'Z-order it to the top
'form.Controls.Add 'rect'
'form.Controls.Add ctrl
'Z-order 'ctrl' to the top
'Center 'ctrl'
'Set focus to it
End Sub
But with WPF I run into problems:
1) I can't add it to the WPF window, because it already has a child.
2) If that child is a Canvas, that's not too bad. I can detect that, and add it to the Canvas. I have to find some way to set its Left, Top etc. properties and Width and Height, since those do not seem to be properties of the Rectangle control but rather extended by the Canvas object -- in XAML they're called Cavnas.Top etc. but Intellisense is not showing them when I try to use it in code.
3) But if it's a StackPanel? Then my rectangle will just be stacked below the other controls! And not covering them! Is there a way around this?
4) And if the window contains only one control and no container control at all?
5) I think there were more problems I ran into. But let's start with these.
Thanks in advance for your help.
1) I can't add it to the WPF window, because it already has a child.
Ah, the evils of codebehind. The solution is not to add it to the visual tree, it is to place it in the visual tree, ready and waiting to pounce, but hide it from the user's view.
Here's a sample you can drop in Kaxaml that demonstrates the point. Set the Lightbox Grid's Visibility to Hidden to access the hidden content.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Viewbox>
<TextBox Text="SIMULATING CONTENT" />
</Viewbox>
<Grid x:Name="Lightbox" Visibility="Visible">
<Rectangle Fill="Black" Opacity=".5"/>
<Border
Margin="100"
Background="white"
BorderBrush="CornflowerBlue"
BorderThickness="4"
CornerRadius="20">
<Viewbox Margin="25">
<TextBox Text="SIMULATING LIGHTBOX"/>
</Viewbox>
</Border>
</Grid>
</Grid>
</Page>
2) (snip) Intellisense is not showing them when I try to use it in code.
Canvas.Top etal are Attached Properties. Attached Properties are extremely convenient and easy to use in XAML, but they are very confusing and hard to use from code. Another reason why codebehind is evil.
3) But if it's a StackPanel? Then my rectangle will just be stacked below the other controls! And not covering them! Is there a way around this?
I redirect you back to 1. There are also many other container controls in WPF. You should investigate them and observe how they control layout. For instance, my use of the Grid was not to make use of its ability to block off sections of UI for controls, but for its ability to layer controls ontop of each other and to stretch them out to their maximum available size for the available space (the viewboxes are just there to zoom the controls instead of stretch them).
4) And if the window contains only one control and no container control at all?
The root of a window would almost always be a container control. But you control that, so if you needed to add controls to the visual tree at runtime you could easily ensure the child of the window is a container control you could deal with.
5) I think there were more problems I ran into. But let's start with these.
No kidding. My number one suggestion for people in your situation is to drop what you're doing and learn about MVVM. The Model-View-ViewModel is a very simple way to code WPF applications that takes advantage of many of the features of WPF--databinding, templating, commands, etc. It allows you to code your logic not in codebehind (RETCH) but in easy to create and test classes.

Retemplating a Button control with custom properties possible?

I made myself a TransparentButton style that makes the Button portion behave the way I want it to (mouseover, enabled, etc), but what I haven't been able to do is set the content correctly in the style. Currently, I manually set everything in <Button.Content> for every button, and clearly that stuff needs to go into the Style. I have set the ContentTemplate for the style to a StackPanel that just contains an Image and a Label. The problem is, I don't know how to specify in my <Button ...> markup the Label's text and the Image's Source. I figured that it had to do with TemplateBinding somehow, but I've been searching like crazy and can't seem to find the information.
So, in summary, I just want a consistent button style where the button content is just a StackPanel of an Image and a Label, and I want to be able to create it in my GUI with something simple like:
<Button Style={DynamicResource TransparentButton}"
Label="Click Me" Image="Images/MyImage.png" />
Any tips would be much appreciated! I hope I'm on the right track here...
In order to create custom properties like this, you'll need to make a CustomControl instead of just using a Button style.
This is fairly easy, though - just make a custom control that subclasses button, and adds your new properties (as dependency properties). This will make it stylable, but also provide you the ability to enforce that those properties are always available, with the syntax you're describing (other than changing <Button to <local:MyButton).
For details, see the Control Authoring Overview on MSDN.

What is the difference between modifying a control by altering the control template and adding sub controls

I am new to WPF and I have a doubt in modifying a control. I came to know that one can modify a control by changing its Control Template. i.e.,
<Button>
<Button.Template>
<ControlTemplate>
...
</ControlTemplate>
</Button.Template>
</Button>
Also we can edit a control utilizing the Content Model. i.e.,
<Button>
<Grid>
<CheckBox>
</CheckBox>
<RadioButton>
</RadioButton>
...
</Grid>
</Button>
What is the difference between these two techniques. Which one is the correct way to customize a control in WPF. Please help me in understanding this better.
The primary difference between these two things, is that the ControlTemplate defines the look of the control. It is not actually placing content inside of it. At some location inside the content control, there should be some form of ContentPresenter. The built in controls are capable of this because they are what is known as 'lookless controls', and any custom controls created should also be lookless. When a control is not templated in a lookless manner but instead has a static layout, then the confusion you have run into can occur.
As for the correct way to do things, it depends on what you are trying to achieve. If you are attempting to change the control, such as the look and feel or the behavior, then using a ControlTemplate, (or DataTemplate, depending on what you are templating), is definitely the way to go. A good example of this is the CheckBox, belive it or not, the CheckBox is actually a ToggleButton (more or less), that through templating displays the togleablity in a bullet. Here's another very good example of how you can use Templates to do some very nifty things.
ControlTemplates should also be applied through Styles, instead of directly set on an element.
If you aren't actually aiming to modify the behavior or look of the control then using the content model is the correct approach.
WPF does this better then Silverlight, though I don't know if there are improvements in SL3.
My rule of thumb is that if it's possible to get what I want without changing the control template then you shouldn't change the control template.
control templates are a way to change the look&feel of a control, for example making a round button of changing the check mark graphics of a checkbox.
adding controls inside a control is simpler, for example place an image inside a button.
Also remember, there is a lot of functionality in the control template, including padding, content alignment, disabled look and more, if you change the control template you will have to add support for all of those features.
Template can be placed into a resource and re-used for another button.
Changing content directly is not re-usable unless you make a UserControl out of it.
Which one you use depends on a concrete task and your personal preferences.

Resources