WPF XAML defined MenuItem reuse starts working, then disappears - wpf

The following simple code attempts to reuse a MenuItem defined in the Window.Resources on two separate Menus.
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<collections:ArrayList x:Key="menuItemValues">
<MenuItem Header="First"/>
<MenuItem Header="Second"/>
<MenuItem Header="Third"/>
</collections:ArrayList>
<MenuItem x:Key="menuItem" x:Shared="False"
ItemsSource="{StaticResource menuItemValues}"
Header="Shared menu item"/>
</Window.Resources>
<StackPanel>
<Menu HorizontalAlignment="Left" VerticalAlignment="Top">
<StaticResource ResourceKey="menuItem"/>
<StaticResource ResourceKey="menuItem"/>
</Menu>
</StackPanel>
</Window>
This starts out great and when you first select the menus, all looks well. The first menu has the desired MenuItems,
So does the second:
But when you navigate back to the first menu, the MenuItems disappear:
Can someone explain why the menu disappears and a way to get this to work?
This was discovered while investigating another SO question that was getting an exception. I tried to use a strategy discussed on another SO question and it seemed to solve the problem until you navigate back to the menu a second time and it disappears.
I have reproduced this issue on 2 separate machines:
Win 10, VS2013 Ult V12.0.40629.00 Update 5, .NET V4.6.0138
Win 7, VS2013 Prem V12.0.31101.00 Update 4, .NET V4.5.51209

This is happening because, while the top-level MenuItem is x:Shared="False", the MenuItem objects in your collection are not. They are declared once each in the ArrayList collection, and then reused in each instance of the menuItem object that's created.
To get the code to work, you'll need to force WPF to create new instances. One option would be to apply the x:Shared="False" to the collection as well. For example:
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<collections:ArrayList x:Key="menuItemValues" x:Shared="False">
<MenuItem Header="First"/>
<MenuItem Header="Second"/>
<MenuItem Header="Third"/>
</collections:ArrayList>
<MenuItem x:Key="menuItem" x:Shared="False"
ItemsSource="{StaticResource menuItemValues}"
Header="Shared menu item"/>
</Window.Resources>
<StackPanel>
<Menu HorizontalAlignment="Left" VerticalAlignment="Top">
<StaticResource ResourceKey="menuItem"/>
<StaticResource ResourceKey="menuItem"/>
</Menu>
</StackPanel>
</Window>
Of course, given that the items are simply given Header values, you could just use the default MenuItem templating behavior, by providing string values instead of MenuItem values. This allows you to reuse the collection itself (which has no underlying inability for reuse):
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<collections:ArrayList x:Key="menuItemValues">
<s:String>First</s:String>
<s:String>Second</s:String>
<s:String>Third</s:String>
</collections:ArrayList>
<MenuItem x:Key="menuItem" x:Shared="False"
ItemsSource="{StaticResource menuItemValues}"
Header="Shared menu item"/>
</Window.Resources>
<StackPanel>
<Menu HorizontalAlignment="Left" VerticalAlignment="Top">
<StaticResource ResourceKey="menuItem"/>
<StaticResource ResourceKey="menuItem"/>
</Menu>
</StackPanel>
</Window>

Related

How do I make sure WPF sub-menuitems don't go out of my main window in restore mode?

How do I make sure my sub menu-items stay within the main window like below in restore mode?
Here is a small portion of my code
<Window x:Name="Main Window" x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Icon="Notepad.ico"
Title="Home" Height="350" Width="860.142">
<Grid>
<Menu x:Name="menu" HorizontalAlignment="Left" Height="22" VerticalAlignment="Top" Width="852" Background="#FFA9D1F4">
<MenuItem Header="_File">
<MenuItem Header="New" Command="New"/>
</MenuItem>
</Menu>
</Grid>
</Window>
Another app.
Note: it may run fine on some computers as pointed out by #G K.

Extended WPF Toolkit zoombox disable zoom and reset

I am trying to use the extended toolkit Zoombox in my WPF Application with NET451.
I am using a tabcontrol for navigation and the items are bound to an observablecollection of ContentControls. When i change tab the content is changed but the zoomparameters are not reset. Also i want to disable zooming for certain tabitems. Binding to properties such as scale, minscale and maxscale does not seem to do the trick. My question is, how can i reset/go to home when changing view?
And how can i disable zoom for cetain tabitems?
XAML Looks like:
<Controls:MetroWindow x:Class="Metrotest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:toolkit="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:Metrotest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Controls:MetroWindow.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
</Controls:MetroWindow.Resources>
<Grid>
<Grid.DataContext>
<local:MainViewModel/>
</Grid.DataContext>
<TabControl ItemsSource="{Binding tabitems}">
<TabControl.ContentTemplate>
<ItemContainerTemplate>
<Controls:FlipView
IsBannerEnabled="False"
ItemsSource="{Binding flipviewitems}" >
<Controls:FlipView.ItemTemplate>
<DataTemplate>
<toolkit:Zoombox ZoomOn="Content" x:Name="zoombox" AutoWrapContentWithViewbox="False" >
<ContentControl Content="{Binding flipview.Content}"/>
</toolkit:Zoombox>
</DataTemplate>
</Controls:FlipView.ItemTemplate>
</Controls:FlipView>
</ItemContainerTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Controls:MetroWindow>

How to add MahApps.Metro Context Menu

I fount this Context Menu style that i want to add to my application.
I put it In Windows.Resources ad add the style (Style="{StaticResource MetroContextMenu}") into my ContextMenu - nothing happen and my ContextMenu
is still the same so i try to put it inside App.XAML ans still my ContextMenu have the old style.
Am i missing something ?
Update
<Controls:MetroWindow x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
WindowStartupLocation="CenterScreen"
Height="670"
Width="1024"
GlowBrush="Black"
ShowMaxRestoreButton="False"
ResizeMode="CanResizeWithGrip"
WindowTransitionsEnabled="False"
TitleCaps="False"
TitleForeground="White"
EnableDWMDropShadow="True">
My context menu:
<ListView.ContextMenu>
<ContextMenu Name="lvFileMenu" Style="{StaticResource MetroContextMenu}">
<MenuItem Name="openCaptureMenuItem" Header="Open file" VerticalAlignment="Center" Height="25">
<MenuItem.Icon>
<Image VerticalAlignment="Center"
Source="pack://application:,,,/Resources/open.ico"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
have you looked at quick start for MahApps?
make sure you have Controls:MetroWindow instead of Window tag

WPF command pattern

I have an application wide flag that is an app setting.
MyApp.Properties.Settings.Default.SoundMuted
The flag is if my apps sound is muted. I have a number of windows where sounds are being played through .Net’s sound player. I was thinking the flag would be wired up to a toolbar button with a command. I was thinking for muting notification to have my classes that play sound wire up to a class (e.g. SoundManager) hanging off the app that implements NotifyPropertyChange. Then if the user clicks the toolbar button, I would set the Muted property in my SoundManager and have all the soundplayer classes get the PropertyChange and mute.
Is there a better pattern for this? Say I could wire all the soundplayers up to the command and that command would fire.
Also is there some slick way to just wire that app setting as a bindable property in xaml?
Although I've always done the same approach as in my other answer in knocking up that example I have realized that it is unnesssasary. You can bind to the Settings object directly, like this:
<Window x:Class="Test_WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyApp"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:Settings x:Key="SettingsRes"/>
</Window.Resources>
<Grid>
<Menu>
<MenuItem Header="Audio">
<MenuItem Header="Mute" IsCheckable="True"
IsChecked="{Binding Path=Default.SoundMuted, Source={StaticResource ResourceKey=SettingsRes}}"/>
</MenuItem>
</Menu>
<TextBlock Text="{Binding Path=Default.SoundMuted, Source={StaticResource ResourceKey=SettingsRes}}" Height="23" HorizontalAlignment="Left" Margin="18,156,0,0" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>
Again, multiple instances of that resource still work well together, e.g. bind the TextBlock to a second resource:
<Window x:Class="Test_WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyApp"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:Settings x:Key="SettingsRes"/>
<local:Settings x:Key="SettingsRes2"/>
</Window.Resources>
<Grid>
<Menu>
<MenuItem Header="Audio">
<MenuItem Header="Mute" IsCheckable="True" IsChecked="{Binding Path=Default.SoundMuted, Source={StaticResource ResourceKey=SettingsRes}}"/>
</MenuItem>
</Menu>
<TextBlock Text="{Binding Path=Default.SoundMuted, Source={StaticResource ResourceKey=SettingsRes2}}" Height="23" HorizontalAlignment="Left" Margin="18,156,0,0" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>
One way is to make a wrapper class to provide bindable access to the static Settings.Default.
I have since realized this is more work than is needed, please see my other answer
namespace MyApp
{
internal sealed class ResourceWrapper
{
public Settings Default
{
get
{
return Settings.Default;
}
}
}
}
Now, we need to add that as a resource somewhere, could be done in App.xaml, here I've done local to the window that is using it, don't forget the namespace:
xmlns:local="clr-namespace:MyApp"
<Window.Resources>
<local:ResourceWrapper x:Key="SettingsWrapper"/>
</Window.Resources>
Now we need to bind to that, this shows it's use in a MenuItem on that same window:
<Menu>
<MenuItem Header="Audio">
<MenuItem Header="Mute" IsCheckable="True"
IsChecked="{Binding Path=Default.SoundMuted, Source={StaticResource ResourceKey=SettingsWrapper}}"/>
</MenuItem>
</Menu>
Like I said, you can add the resource at the app level or you can create multiple of these ResourceWrappers in different windows/controls, they will all point to the same static underneith.
That full xaml for window with a test TextBlock:
<Window x:Class="Test_WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyApp"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:ResourceWrapper x:Key="SettingsWrapper"/>
</Window.Resources>
<Grid>
<Menu>
<MenuItem Header="Audio">
<MenuItem Header="Mute" IsCheckable="True" IsChecked="{Binding Path=Default.SoundMuted, Source={StaticResource ResourceKey=SettingsWrapper}}"/>
</MenuItem>
</Menu>
<TextBlock Text="{Binding Path=Default.SoundMuted, Source={StaticResource ResourceKey=SettingsWrapper}}" Height="23" HorizontalAlignment="Left" Margin="18,156,0,0" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>

How can I display my user control in the MainWindow?

I'm trying to build a small MVVM test application, but can't really figure how to display my user control in the MainWindow.
My Solution Explorer:
I got a resource dictionary:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MVVM.ViewModel"
xmlns:vw="clr-namespace:MVVM.View">
<DataTemplate DataType="{x:Type vm:ViewModel}">
<vw:View />
</DataTemplate>
</ResourceDictionary>
I got my view:
<UserControl x:Class="MVVM.View.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<DataTemplate x:Key="PersonTemplate">
<StackPanel>
<TextBlock Text="{Binding FirstName}" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<ListBox ItemsSource="{Binding Path=Persons}"
ItemTemplate="{StaticResource PersonTemplate}" />
</UserControl>
and My MainWindow
<Window x:Class="MVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MVVM.ViewModel"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary Source="MainWindowResources.xaml" />
</Window.Resources>
<Grid>
</Grid>
</Window>
The most obvious and easiest way is to add the ContentControl element:
<Grid>
<ContentControl x:Name="mainContentControl" />
</Grid>
And after that set the Content property of this control to your view model, and the corresponding view will be loaded and applied automatically:
this.mainContentControl.Content = new ViewModel.ViewModel();
But I would prefer to use another way without datatemplates:
<Grid>
<vw:View x:Name="mainView"/>
</Grid>
this.mainView.DataContext = new ViewModel.ViewModel();
Build your VS2010 solution, then, go to your MainWindow's XAML.
On the left, there is a toolbar with button "Toolbox"
Open it, it contains all the possible WPF controls you could add to your UI
Your UserControl should appear on top of the list (in a category probably named "MVVM Controls"), just drag&drop it to your UI :)

Resources