Conditional loading of WPF Control with only XAML code - wpf

Suppose i have a complex UI. Depending on the available MODEL data, some of the controls make no sense. I would like to have an option to 'disable' them. By 'disable' I mean that I don't want their DataBinding to happen as their ViewModel is unsafe\ undefined.
In this post, it was suggested to use DataContentSelector.
I wonder if there's a different approach that doesn't use code outside of the xaml. For example, an implementation using VisualState, where the VisualState will set the content to some empty box if the data is empty.
Thanks

The fully MVVM-compliant way in which I have been achieving this is as follows:
Say you have a view model of type MainViewModel which is being displayed by a view of type MainView.
MainView needs to conditionally show a sub-view of type SecondaryView, depending on the value of some controlling property in MainViewModel: when the controlling property has a certain value, the view is to be shown, when the controlling property has another value, the view is not to be shown, and when it is not shown, we do not want it to just be hidden, we want it to not even exist.
Introduce a new view model of type SecondaryViewModel. It can even be an empty class, though you will probably find some useful functionality to put in it.
In your MainViewModel introduce property MySecondaryViewModel of type SecondaryViewModel. This will be the controlling property. Initially, let the value of this property be null.
In the XAML of MainView, define a ContentControl as follows:
<ContentControl Content="{Binding MySecondaryViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type myViewModels:SecondaryViewModel}">
<myViews:SecondaryView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
So, now, the nullness of the controlling property MySecondaryViewModel controls the existence of SecondaryView:
For as long as the value of the controlling property is null, the framework does not create any view to display it, and the ContentControl is empty. (It has no content.)
If you assign an instance of SecondaryViewModel to the property, the framework will create an instance of SecondaryView to show it.
If you later set the controlling property to null again, the instance of SecondaryView will be destroyed.

I use this trick: ( sorry for bad English)
First i bind my DataContext to my VM and add a Converter to it
<Grid DataContext={Binding myvm, Converter={StaticResource mySwitchOfConverter}}
and in my converter i have something like this :
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if(Util.SwitchContext == true)
return value;
return null;
}
Util.SwitchContext is a static property witch i'll set and unset it in my code.

<UserControl.Resources>
...
<DataTemplate x:Key="someControl" ...>
<DataTemplate x:Key="somePlaceholderControl" ...>
</UserControl.Resources>
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="ContentTemplate" Value="{StaticResource somePlaceholderControl}" />
<Style.Triggers>
<DataTrigger Binding="{Binding SomeTriggerProperty}" Value="SomeTriggerValue">
<Setter Property="ContentTemplate" Value="{StaticResource someControl}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>

Related

The attachable property 'IsEnabled' was not found in type 'TextBox'

From my understanding of attached properties, I believe that I can set a property value that will apply to all of the children of a container that match the type. For instance, if I have a number of TextBoxes in a StackPanel, then I can disable them all by setting the TextBox.IsEnabled property to false in the StackPanel's declaration:
<StackPanel TextBox.IsEnabled="False" Orientation="Horizontal">
...
</StackPanel>
I tried this in Visual Studio, and the Xaml designer greyed-out the TextBoxes in the StackPanel exactly as expected, but when I tried to compile, I ran into the error:
The attachable property 'IsEnabled' was not found in type 'TextBox'
Have I misunderstood attached properties? Do they only go from the ancestor to the child? If so, is there a way to do what I am trying, ie, to set all child TextBoxes IsEnabled property to false?
Thanks for any pointers
Yes, attached properties allow to set a property value on the parent and the children inherit that value. On the other hand TextBox.IsEnabled is not an attached property and so you cannot do what you want.
Perhaps it is possible to get what you want with some custom panels and/or custom attached properties programming.
However you could also get the same result using a Style where you can also bind the IsEnabled property to your custom logic if you need.
<StackPanel Width="200" Height="50" >
<StackPanel.Resources>
<Style TargetType="TextBox">
<Setter Property="IsEnabled" Value="False" />
</Style>
</StackPanel.Resources>
<TextBox Text="one" />
<TextBox Text="two" />
</StackPanel>

WPF ComboBox: How to you utilise a generic ItemContainerStyle with binding

I want to utilise a generic style for my ComboBoxItem content and have the text content bound to different properties on my underlying class. So this is the best I can come up with but the bindings are hard coded. So for every class bound to a combobox using this ItemContainerStyle I'd have to implement a "MainText" and "SubText" property.
Question is, is there a way to have the binding soft coded so where the style referenced from a combobox I can specify which string properties of the underlying class are used.
<Style TargetType="{x:Type ComboBoxItem}" x:Key="ComboBoxItemStyleA1">
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border x:Name="BB" Padding="8,3,8,3" Background="DarkGreen">
<StackPanel Margin="0">
<TextBlock Foreground="White" FontSize="16" Text="{Binding MainText}"/>
<TextBlock Foreground="White" FontSize="8" Text="{Binding SubText}"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" TargetName="BB" Value="#FF256294"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And to use the style...
<ComboBox ItemsSource="{Binding Items}"
ItemContainerStyle="{StaticResource ComboBoxItemStyleA1}" />
Further to dowhilefor's answer (many many thanks - WPF is great but sure is a voyage of discovery)
I used a data template to define the cell look originally - and then wanted to use a comboboxitem based style with a control template defined where I could specify the onmouseover triggers. i.e. these were to change the background color etc.
Butj
a) I couldn't remove the Border section of the template above - the triggers are tied to it by targettype="BB". so I kind of wanted to get the trigger bound to the container such that the datatemplate would pick up the background from the template binding but not sure how to get this plumbed in.
b) I realised that even if I comment out the BB specific bindings on the triggers just to get it to run - the combobox doesn't find and use the DataTemplate I defined. Seems that defining the controltemplate in my comboboxitemstyle stops it picking up the datatemplate.
I hope I make sense here - bottom line is I just want a style that I can apply with triggers in that set the background color of my cobobox item. It should not know what the data is - i.e. be able to plug in a datatemplate that will (template ?) bind to this background color.
Many thanks for the very fast response.
btw I'm using ItemContainerStyle in conjuction with ItemTemplate so I can have a different representation in the dropdown to what appears in the combobox list
First of all don't use the ItemContainerStyle for that. To be more precise never have any Bindings to the datacontext inside an ItemContainerStyle, at least try not. Why? The Style is used for defining the appearance of a combobox item disregarding the content. If you want to define how the content should look like, you use a DataTemplate for that. There are multiple ways to tell the combobox where he can find a proper DataTemplate for the Data you supply. Checkout the property ItemTemplate, ItemTemplateSelector and search for implicit styles, to find out more about them.
So to your problem, create one ItemContainerStyle for you combobox (if you really have to anymore) which doesn't care about the object that will be put into. Now you still need to provide multiple DataTemplates each and everyone with the knowledge of the data object that you want to be templated. There is no way around it, there is no soft databinding. Just try to keep your templates small and simple. If for some reason you need the exact same template, but your properties are just named differently, why not use a wrapper item for the DataContext with the properties Caption, Description and you can decide in code how these properties are filled with your real data wrapped into this object.

WPF-MVVM: Prevent the user select something else in the listbox

I've a small WPF application(.Net 3.5 :/ ).
In this application I've a listbox, which allows me to select an element to edit on the right part of the application.
If the right part is invalid, I need to prevent the user changing the selection of elements.
I've made a lot of search on this:
Some were telling to change the background/brush to make it look like the selection is not possible(but the selection is still possible)
Some other were telling me to update the IsFocusable property of sub elements:
<ListBox itemsSoutces={Binding Test}>
<ListBoxt.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Focusable" Value="False"/>
</Style>
</listBox.ItemContainerStyle>
</ListBox>
The problem with this solution is that this value has to comes from a value of my ViewModel, and I don't know(nor it's possible) to bind the Value of the Style Setter to a property of my ViewModel?
Is it possible? How?
I would simply disable the listbox if the right part is invalid by setting IsEnabled to false. If your ViewModel contains a corresponding property, you can bind to that:
<ListBox IsEnabled="{Binding IsValid}" ...> ... </ListBox>

How do I apply a Style Setter for ListBoxItems of a certain DataType?

My WPF ListBox contains two types of object: Product and Brand.
I want my Products selectable. I want my Brands not selectable.
Each of the two types has its own DataTemplate.
By default, anything may be selected:
<ListBox ... >
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:Product}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type local:Brand}">
...
</DataTemplate>
</ListBox.Resources>
</ListBox>
I can set Focusable with a Setter, but then nothing may be selected:
<ListBox ... >
<ListBox.Resources>
...
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Focusable" Value="False" />
</Style>
</ListBox.Resources>
</ListBox>
I cannot put the Setter within the DataTemplate.
I cannot put a DataType onto the Style.
How do I style only the ListBoxItems of type Brand?
Thanks to the StyleSelector class you can attach styles depending on type of data for the ItemContainerStyle. There is a really good example here : http://www.telerik.com/help/wpf/common-data-binding-style-selectors.html
Can you use a data trigger on your ListBoxItem style? If so, bind to the DataContext (your class) and use a value converter to test the type. If it's the one you're interested in, style the ListBoxItem so that it cannot appear selected.
I don't think you can disallow selection of an item in a Selector (parent of ListBox) without codebehind or a custom Behavior.

WPF - Combobox with a Prompt

I'm looking for a way to add a "SELECT An Item" to a combobox that does not have an item selected. This would be different than a selected item as a default.
I want the combobox to say "SELECT an Item" and be bound to one list for the possible selections and another model for the selected item.
I'd prefer a style that I can apply to multiple comboboxes and have a way to set the prompt. I've seen something similiar at http://marlongrech.wordpress.com/2008/03/09/input-prompt-support-in-wpf/, but the it does not work smoothly, requiring 2 clicks to get to the list.
Thanks!
You could use the adorner solution you linked to with a couple of changes, or you could do this with a style and converter.
Adorner solution
The adorner solution is more complex, but has a better interface and encapsulation. The changes you would need to make are straightforward but possibly difficult if you're not a WPF expert. They are:
Recognize ComboBox as another special case (like TextBox). Subscribe to its SelectedItemChanged, and update adorner visibility using SelectedItem==null.
Don't handle input events (HitTestVisible=False, Focusable=False, etc)
In this case, your ComboBox style will be very simple, just setting the attached property.
Style and converter
Doing it with a style and converter may be simpler for you. Here is the body of the converter:
object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value==null ? Visibility.Visible : Visibility.Hidden;
}
Your style will replace the default ComboBox style and contain a copy of the ControlTemplate from the theme, wrapped with something like this (or use an adorner):
<Style TargetType="{x:Class ComboBox}">
<Style.Setters>
<Setter Property="local:MyInputPromptClass.PromptText" Value="SELECT an item" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Class ComboBox}">
<Grid>
... existing XAML from theme ControlTemplate ...
<TextBlock
Text="{Binding local:MyInputPromptClass.PromptText, RelativeSource={RelativeSource TemplatedParent}}"
Visibility="{Binding SelectedItem, Converter={x:Static local:MyInputPromptClass.Converter}, RelativeSource={RelativeSource TemplatedParent}}"
HitTestVisible="False" Focusable="False"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
This solution is less satisfying than the other, since by copying the default ComboBox template from a theme you end up with an app that doesn't track the current Windows theme. It's possible to get around this using multiple ControlTemplates along with StaticResource and some tricky binding, but at that point I would recommend just using the adorner and attached property.

Resources