Can I implement such rule in the WPF context menu in XAML? - wpf

Recently I am developing a context menu in WPF with the following requirement,
If all the menu items in the context menu have Visibility Hidden, set the context menu visibility to Hidden.
I have a solution myself for this in a DataTemplate, which is to set ContextMenu Hidden as default, and use a Trigger for each menu item to check their visibility, if any of them is visible, trigger the ContextMenu visibility into visible.
So the code is like
<DataTemplate>
<ContextMenu Visibility="Hidden" x:Name="contextMenu">
<MenuItem x:Name="menuItem1" Visibility="{Binding somebinding}" />
<MenuItem x:Name="menuItem2" Visibliity="{Binding somebinding}" />
</ContextMenu>
<DataTemplate.Trigger>
<Trigger SourceName="menuItem1" Propert"Visibility" Value="Visible">
<Setter TargetName="contextMenu" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger SourceName="menuItem2" Propert"Visibility" Value="Visible">
<Setter TargetName="contextMenu" Property="Visibility" Value="Visible" />
</Trigger>
</DataTemplate.Trigger>
</DataTemplate>
my problem is that this really depends on the control which owns this context menu (in this case it is the DataTemplate). We actually want to make this context menu behavior independent and put it in a resource so that other controls can share with it.
I am trying to do it in a Style trigger of the context menu itself, but in a Style trigger I can't use the target name and source name.
Can anyone help me think of a better solution? Thanks.
S.
UPDATE:
Looking at the examples in the link from Nikolay, I implemented a converter as below
<ContextMenu x:Name="contextMenu" Visibility={Binding PATH=Items, Converter={StaticResource Converter}>
<MenuItem x:Name="menuItem1" Visibility="{Binding somebinding}" />
<MenuItem x:Name="menuItem2" Visibliity="{Binding somebinding}" />
</ContextMenu>
In the converter, it checks the visibility of each menu item and set the proper visibility value of the context menu.
But the problem I found is that WPF evaluates the bindings from top to bottom, so ContextMenu is evaluated first and then the MenuItem, in this case my converter doesn't work because at the time of binding, the Items is still None.
Any tips guys?

look at the following post, it'll give you ideas.. Visibility is toggled on different controls, but the approach would be similar.
Implementing "Rename" from a ContextMenu

Solution with converter does not work because you bind context menu visibility to items, but items collections never changes after load and binding is not being re-evaluated then visibility of subitem changes.
I don't see any easy ways to achieve this in Xaml only, but you can easily do it with code. For example, you can derive from ContextMenu and in derived class you can subscribe to changes of visibility of all subitems (via depependecy properties change notifications) and change its visibility accordingly.
Hacky way to do it in xaml only - use multibinding for ContextMenu.Visibility and bind to both items and your somebinding but use only items actually. This way wpf will call your visibility converter any time somebinding changes.

Related

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 to add a click handler to MenuItems created by a data binding?

and now the details...
First if all this is my first encounter with WPF so please forgive I am missing something obvious etc.
What I have is a Menu with Menu Items bound to a DataView each menu item is a Row in DataView MenuItem Text is set to a Field in Row... menu is populated well and I can see all rows listed there! but when clicked nothing happens.
My Problem is how I can assign a click event to Menu Item, below is the XAML of ItemPresenter of parent menuitem
<MenuItem Name="mnuRowsDropDown" Header="All Rows ▼"
Height="23"
Loaded="mnuNotesDropDown_Loaded" ItemsSource="{Binding Path=Title, Mode=OneWay}" >
<MenuItem.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Header" Value="{Binding Title}"/>
<!--<Setter Property="MenuItem.Click" Value="" />-->
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</MenuItem>
As you can see here I commented the Click Code because designed said it is an error
There's a special setter for that called EventSetter, use that instead. (When creating MenuItems dynamically it may be of interest to use commands instead as those can easily be included in the object you use for the binding along with the Header)

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.

WPF Style Active Item

I am attempting to create a reusable navigation style Custom Control in WPF, like a navigation bar on a website. It will contain links to all the main Pages in my app. This control can go on top of all my Pages in my NavigationWindow. Giving a nice consistent look and feel across pages, like a website.
My issue is in styling the current page's link differently than the other pages' links, so that you can quickly glance at it and see which page you're on. Since the control is the same on each Page, I need to tell it which page is "active" and have it style that link appropriately.
My first thought was to simply place Is<Page>Active properties on the control, one for each page, and then set the appropriate property to true on the page. (Or I could use one property that accepts an Enum value instead of having many properties, either way)
Example:
<local:Header IsHomePageActive="True" />
Now in the control template for my Header Custom Control, I can create a DataTrigger that watches this property:
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource FindAncestor, AncestorType={x:Type local:Header}}, Path=IsHomePageActive}" Value="true">
<Setter ... />
<Setter ... />
<Setter ... />
</DataTrigger>
</Style>
After all that background, here's my question. This works, but I'm going to have to duplicate that DataTrigger, and all the Setters in it, for every single Page I have, because the Trigger has to directly reference the "IsHomePageActive" property which only applies to the one link. So I need a different Style for every link, even though the actual STYLE its describing is exactly the same (by which I mean, the Setters are the same). The only difference is what property the trigger is watching.
Is there some way to do this (or something with the same end result) without ending up with hundreds of lines of duplicated XAML?
How about using a master/detail pattern () with a listbox(say) as the master and your pages as the details.
Then specify your style for the selected item in the list.
The page will change when a different list item is selected and it will look different to the other items.
if you have a dependency property such as List Pages where Page inherits from UserControl and has a string Title you can use
<ListBox
ItemsSource="{Binding Pages}"
IsSynchronizedWithCurrentItem="true">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ScrollViewer VerticalScrollBarVisibility="Auto"
Content="{Binding Pages/}" />
Then just set style for the selected item on the listbox

Resources