WPF Extending Window - wpf

So I have a bunch of WPF Windows in my Windows Application.
I want each window to have a context menu that has one item ( see the code bellow ). However, I don't feel like copying and pasting this code everywhere. I would like to somehow extend the WPF Window class ( and call it PrintableWindow ) and somehow make every window be an extension of the PrintableWindow ....Is this possible ???
<Window.ContextMenu>
<ContextMenu>
<ContextMenu.Items>
<MenuItem Header="Print"
Click="mnuPrint_Click">
</MenuItem>
</ContextMenu.Items>
</ContextMenu>
</Window.ContextMenu>

You shouldn't need to extend window to do accomplish this, just created a global style for all windows.
In your app.xaml file:
<Style TargetType="{x:Type Window}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<ContextMenu.Items>
<MenuItem Header="Print" Command="Print" />
</ContextMenu.Items>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
You will have to create a handler for the print command in the window if you want to print from a particular window. Even better, if you are using a view model you can just bind to your command.

Create your own Window class called PrintableWindow which derives from
Window and then modify any other Window instances to derive from your PrintableWindow versus Window. This way all Window instances will be of type PrintableWindow which has your ContextMenu and handler associated with it.

Related

Why does my Ribbon Menu Button Popup disappear when I apply this Style?

Here is my XAML:
<Ribbon x:Name="ribbonMain" Height="200" ContextMenu="{x:Null}" VerticalAlignment="Top" ShowQuickAccessToolBarOnTop="False" >
<RibbonTab x:Name="ribbonTabMain" Header="Test Tab" ContextMenu="{x:Null}" >
<RibbonGroup x:Name="ribbonGroupMain" Header="Test Group" ContextMenu="{x:Null}">
<RibbonButton x:Name="ribbonButtonMain" Label="Test Button" ContextMenu="{x:Null}" />
</RibbonGroup>
<RibbonGroup x:Name="ribbonGroupMain2" Header="Test Group 2" ContextMenu="{x:Null}">
<RibbonMenuButton ContextMenu="{x:Null}" Name="ribbonMenuButtonMain" Label="Menu Button">
<RibbonMenuItem ContextMenu="{x:Null}" Name="ribbonMenuItemMain" Header="Menu Item"></RibbonMenuItem>
<RibbonMenuItem ContextMenu="{x:Null}" Name="ribbonMenuItemMain2" Header="Menu Item 2"></RibbonMenuItem>
</RibbonMenuButton>
</RibbonGroup>
</RibbonTab>
</Ribbon>
I then run this C# Code to get the Ribbon Menu Button Default Control Template:
string ribbonMenuButtonControlTemplate = XamlWriter.Save(ribbonMenuButtonMain.Template);
After that I set the x:Name and x:Key properties of the Control Template to something and then put that string of XAML in this:
<Style TargetType="RibbonMenuButton"
<Setter Property="Template">
<Setter.Value>
{DefaultControlTemplateHere}
</Setter.Value>
</Setter>
</Style>
Last I put that Style in my <Window.Resources>.
I wanted to alter the Style from there, but then I realized that the popup just wasn't working anymore.
I expected nothing to change. Seems I was mistaken.
Why does this happen?
Note:
I've tried running this code to see if the popup would open:
if (!ribbonMenuButtonMain.IsDropDownOpen)
{
ribbonMenuButtonMain.IsDropDownOpen = true;
}
With no Style applied that code runs fine and the popup opens.
But with the Style I get this exception:
System.InvalidOperationException: 'This Visual is not connected to a
PresentationSource.'
The XamlWriter.Save method has some serialization limitations that are mentioned here. One of them being that;
Common references to objects made by various markup extension formats, such as StaticResource or Binding, will be dereferenced by the serialization process. These were already dereferenced at the time that in-memory objects were created by the application runtime, and the Save logic does not revisit the original XAML to restore such references to the serialized output.
So your generated template is missing a TemplateBinding to the IsOpen property of the Popup:
<Popup ... IsOpen="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsDropDownOpen}">
You may extract the default template including any bindings from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\WPF\System.Windows.Controls.Ribbon.dll using a decompiler such as for example dotPeek.

WPF Command Binding ItemsControl in Styles

I have a style in the Textboxstyles.xaml as following
<Style x:Key="EmptyItemsControlUsabilityDashboard2017Style" TargetType="ItemsControl">
<Style.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Control">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Image Height="12" Width="12" Source="/YoLo;component/Resources/Images/link.png" Margin="0,3,0,0" />
<TextBlock x:Name="EmptyCollectionTextBox" Text="{x:Static UsabilityDashboard2017Loc:DashboardUsability2017Resource.lblNumNotDefined}"
Style="{StaticResource UsabilityDashboard2017TextBoxStyle}"
HorizontalAlignment="Center"
Margin="5,25,0,25"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
and I have used it inside another Xaml file as following
<ItemsControl ItemsSource="{Binding YoLoViewModelsCollection}" Name="YoLoViewModelsItemSource" Style="{StaticResource EmptyItemsControlUsabilityDashboard2017Style}">
Now it shows a text box that the this collection is empty but how can I set command bindings on the text block named "EmptyCollectionTextBox" inside the style that user user click it executes a command?
I have already seen the custom commands but somehow they are not working.
There's actually a lot of stuff wrong with this code. First of all I have no idea what that trigger is supposed to be doing, it looks like the controltemplate will only be set if there are no elements in the list?
Secondly, it looks like you're trying to represent each element in the collection with an image and text, all in an ItemsControl. You don't do that by templating the entire control, you do it by templating the ItemTemplate. And you use a DataTemplate, not a ControlTemplate:
Now going back to your actual question, you want notification whenever the TextBlock is clicked on. There are a multitude of different ways to do this, but for this case you may as well replace that TextBlock with a Button, and then override the Template with a ControlTemplate that represents it as a TextBlock. This gives you the best of both worlds: from the GUI's perspective it's really still a TextBlock, but you still get all the button click notifications and Command handler etc:
<Image />
<Button Command="{Binding ClickedCommand}" Cursor="Hand">
<Button.Template>
<ControlTemplate>
<TextBlock Text="Text Binding Goes Here" />
</ControlTemplate>
</Button.Template>
</Button>
This particular example assumes that the items in your collection have a command handler called "ClickedCommand". In practice your handler might reside in the parent class (e.g. the main window's view model), in which case you'd need to give your main window a x:Name (e.g. "_this") and bind to that instead, passing the item in as the CommandParameter so it knows which one was clicked:
<Button Command="{Binding ElementName=_this, Path=DataContext.ClickedCommand}" CommandParameter="{Binding}" Cursor="Hand">
And then your main view model has a handler that looks something like this:
public ICommand ClickedCommand { get { return new RelayCommand<YourCollectionItemType>(OnClicked); } }
private void OnClicked(YourCollectionItemType item)
{
}

Using a user control as a context menu in WPF

I've created a WPF user control that contains some grids, buttons, and sliders. I'd like to use this control as (or in place of) a context menu in my main application window. When a user right-clicks the mouse button, I'd like my user control to be displayed, rather than a normal looking context menu with standard menu items.
What's the best approach to take in displaying a user defined WPF control in place of a context menu?
You could define the ControlTemplate of a ContextMenu however you want. Try this:
<Window ... xmlns:local="clr-namespace:WpfApplication1">
<Grid Background="Transparent">
<StackPanel.ContextMenu>
<ContextMenu>
<ContextMenu.Template>
<ControlTemplate TargetType="ContextMenu">
<local:UserControl1 />
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
</StackPanel.ContextMenu>
Just add the control to the ContextMenu. For example:
<Window>
<Window.ContextMenu>
<ContextMenu>
<local:YourUserControl />
</ContextMenu>
</Window.ContextMenu>
</Window>

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

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.

Images in XAML ResourceDictionary disappear on ToolBar when Menu opens

I have started to move various common Images into a ResourceDictionary and noticed an odd behavior in my WPF application. If the Image is used in a MenuItem and in a Button on a ToolBar, when I open the Menu the image disappears on the Button.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Image x:Key="NewImage"
Source="/SomeApplication;component/Resources/NewDocumentHS.png"
Stretch="None"/>
<!-- ... -->
Relevant XAML from the Window:
<Menu>
<MenuItem Header="_File">
<MenuItem Header="_New"
Command="{Binding NewCommand}"
Icon="{DynamicResource NewImage}" />
<!-- ... -->
<ToolBarTray>
<ToolBar>
<Button Command="{Binding NewCommand}"
Content="{DynamicResource NewImage}" />
I assume this is a caveat of resources in a ResourceDictionary, but I am unable to discover the appropriate fix for this. Behavior occurs with both StaticResource and DynamicResource. It also doesn't appear to be affected by if the ResourceDictionary stands on its own or if it is merged with others. No other resource shares that key either.
Edit: Additionally, adding PresentationOptions:Freeze="True" to the images did not change the situation.
The Image class is a visual, so it can only appear in the visual tree in one location. Therefore, you cannot share it among multiple MenuItems/Buttons/etc.
You can however share the ImageSource (i.e. Image.Source) value.
In WPF, I believe you can use x:Shared="False" to force WPF to create a new instance for each request though.
You cannot use an Image control in multiple places, it can only be appear in the Visual Tree at one place, so if the call to the resource is made the image is being snatched from the previous owner.
Edit: x:Shared="False" Is obviously a better solution than all of my suggestions below, i wonder why such an important property does not show up in the Intellisense -_-
This behaviour is a bit of a pain, i normally use to predefine a IconStyle and the BitmapImages for the Source of the images but create new Images for every MenuItem where i might need it.
You can also create a DataTemplate for your Icon:
Resources:
<Style x:Key="IconImageStyle" TargetType="{x:Type Image}">
<Setter Property="MaxWidth" Value="16"/>
<Setter Property="MaxHeight" Value="16"/>
</Style>
<DataTemplate x:Key="Icon_Close_Template">
<Image Style="{StaticResource IconImageStyle}"
Source="pack://application:,,,/Images/Close.ico"/>
</DataTemplate>
Usage:
<Menu>
<MenuItem Header="File">
<MenuItem Header="Close">
<MenuItem.Icon>
<ContentPresenter ContentTemplate="{StaticResource Icon_Close_Template}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Close">
<MenuItem.Icon>
<ContentPresenter ContentTemplate="{StaticResource Icon_Close_Template}"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
Since templates are created via factories this will work, still significantly inflates the XAML though...
To get around this you could for example write a markup extension, this one is very simple and only copies the values of the Source and Style properties, you could also use reflection or other means to create a complete copy:
[MarkupExtensionReturnType(typeof(object))]
public class IconExtension : MarkupExtension
{
private Image icon;
public Image Icon
{
get { return icon; }
set { icon = value; }
}
public IconExtension() { }
public IconExtension(Image icon)
{
Icon = icon;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (Icon == null) throw new ArgumentNullException("Icon");
return new Image() { Source = Icon.Source, Style = Icon.Style };
}
}
Can be used like this:
<Style x:Key="IconImageStyle" TargetType="{x:Type Image}">
<Setter Property="MaxWidth" Value="16"/>
<Setter Property="MaxHeight" Value="16"/>
</Style>
<Image x:Key="Icon_Close" Style="{StaticResource IconImageStyle}" Source="pack://application:,,,/Images/Close.ico"/>
<!-- ... -->
<MenuItem Header="File">
<MenuItem Header="Close" Icon="{m:Icon {StaticResource Icon_Close}}"/>
<MenuItem Header="Close" Icon="{m:Icon {StaticResource Icon_Close}}"/>
</MenuItem>

Resources