WPF command pattern - wpf

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>

Related

WPF - DataBinding (Command is not being executed)

I am writing a WPF application where I wish to implement the MVM pattern.
My code is currently set up like this:
This is my view model class where I am exposing the command I wish to be executed as a property
```
public HomeScreenViewModel(NavigationService mw)
{
this.mw = mw;
}
private RelayCommand _addStudent;
public ICommand AddStudent
{
get
{
if (_addStudent == null)
{
_addStudent = new RelayCommand(param => this.OpenNewStudents(), param => true);
}
return _addStudent;
}
}
This is my xaml containing the layout of the corresponding view
<Page x:Class="test.HomeScreen"
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"
xmlns:local="clr-namespace:test"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="1300"
Title="HomeScreen">
<WrapPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<WrapPanel HorizontalAlignment="Center" Height="300" Margin="0,0,0,0" VerticalAlignment="Center" Width="1300">
<Button Content="AddStudent" Command="{Binding AddCommand}" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<TextBox Text="{Binding Path=HI}" Width="200" Height="200"/>
</WrapPanel>
<Canvas Margin="350, 100,0, 0" Height="300" Width="1350" VerticalAlignment="Center" HorizontalAlignment="Center">
<Image Source="Logo.PNG"/>
</Canvas>
</WrapPanel>
I have set the command of the top button using the binding syntax in the WrapPanel.
This HomeScreen page is linked from a naviagation window and is the source of the navigation window (xaml file):
<NavigationWindow x:Class="test.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:test"
mc:Ignorable="d"
Title="School Management App" Height="350" Width="1250"
Source="HomeScreen.xaml">
</NavigationWindow>
I have my HomeScreen view constructor set like this:
public partial class HomeScreen : Page
{
public HomeScreen()
{
InitializeComponent();
this.DataContext = new HomeScreenViewModel(this.NavigationService);
}
}
My infrastructure is set up as I have described above and my implementation of relay command is taken from this site https://msdn.microsoft.com/en-us/magazine/dd419663.aspx.
When I run my application and I press the corresponding button, nothing happens. I was wondering if someone could help me figure out what is wrong and why the command binding isn't working. It would be much appreciated, thanks :)
as a side note, could someone give me some tips on what the best way to debug wpf applications and figure out what bindings exist at run time.
Thanks :)
your command name is AddStudent, but you use AddCommand in xaml. Just correct the name:
Command="{Binding AddStudent}"

WPF XAML defined MenuItem reuse starts working, then disappears

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>

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 Project has wrong version of OpenGL

I have two separate WPF projects.
My goal: Alter project B to have the same OpenGL object instance version as project A.
Both instantiate a variable 'gl' at some point with the following line:
OpenGL gl = args.OpenGL;
After setting a breakpoint and check 'gl' for its object properties, I see the following in project A:
Version = "4.4.13084 Compatibility Profile Context 14.301.1001.0"
Yet in project B, I see the following:
Version = "1.1.0"
Concerning 'args' both projects instantiate gl in the following method:
private void OpenGLControl_OpenGLInitialized(object sender, OpenGLEventArgs args)
Both projects call that method with:
((SharpGL.WPF.OpenGLControl)(target)).OpenGLInitialized += new SharpGL.SceneGraph.OpenGLEventHandler(this.OpenGLControl_OpenGLInitialized);
Notice that 'args' is not being passed in from here explicitly. I figured function must be called in a deeper context like OpenGLEventHandler. However, I noticed that args is a parameter to that function as well.
public delegate void OpenGLEventHandler(object sender, OpenGLEventArgs args);
I don't have access to source code for OpenGLEventHandler since it is in SceneGraph.dll
I am wondering if the args variable is determined by a .config or .xaml file since my project is a WPF being run from Visual Studio. However, there isn't much differences in the files concerning OpenGL.
Project A's MainWindow.xaml:
<Window x:Class="ObjectLoadingSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Object LoadingSample" Height="600" Width="800"
xmlns:sharpGL="clr-namespace:SharpGL.WPF;assembly=SharpGL.WPF">
<Grid>
<DockPanel>
<ToolBarPanel DockPanel.Dock="Top">
<Menu>
<MenuItem Header="_File">
<MenuItem x:Name="fileOpenItem" Header="_Open..." Click="fileOpenItem_Click" />
</MenuItem>
</Menu>
</ToolBarPanel>
<ToolBarTray DockPanel.Dock="Top">
<ToolBar>
<Label Target="{Binding ElementName=textBoxScale}">Scale: </Label>
<TextBox x:Name="textBoxScale" Width="60" IsEnabled="False">1</TextBox>
<CheckBox x:Name="checkBoxAutoScale" IsChecked="True" IsEnabled="False">Auto</CheckBox>
<Separator />
<Label Target="{Binding ElementName=comboBoxRenderMode}">Render Mode: </Label>
<ComboBox x:Name="comboBoxRenderMode" Width="100" SelectedIndex="1">
<ComboBoxItem>Immediate</ComboBoxItem>
<ComboBoxItem>Retained</ComboBoxItem>
</ComboBox>
<Label Target="{Binding ElementName=comboBoxPolygonMode}">Polygon Mode:</Label>
<ComboBox x:Name="comboBoxPolygonMode" Width="100" SelectedIndex="2" SelectionChanged="comboBoxPolygonMode_SelectionChanged">
<ComboBoxItem>Points</ComboBoxItem>
<ComboBoxItem>Lines</ComboBoxItem>
<ComboBoxItem>Polygons</ComboBoxItem>
</ComboBox>
</ToolBar>
</ToolBarTray>
<sharpGL:OpenGLControl x:Name="openGlCtrl"
OpenGLDraw="OpenGLControl_OpenGLDraw" OpenGLInitialized="OpenGLControl_OpenGLInitialized"
RenderContextType="FBO" Resized="OpenGLControl_Resized" />
</DockPanel>
</Grid>
</Window>
Project B's MainWindow.xaml
<Window x:Class="ProjectBeta.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sharpGL="clr-namespace:SharpGL.WPF;assembly=SharpGL.WPF"
Title="MainWindow" Height="800" Width="800">
<Grid>
<sharpGL:OpenGLControl OpenGLDraw="OpenGLControl_OpenGLDraw" OpenGLVersion="OpenGL4_3" OpenGLInitialized="OpenGLControl_OpenGLInitialized" DrawFPS="True" Margin="-3,0,3,0" />
</Grid>
</Window>
There are other files like App.config, App.xaml, and Settings.settings, however, I don't know which ones would be most useful to share.

Binding one dependency property to another

I have a custom Tab Control that I have created, but I am having an issue. I have an Editable TextBox as part of the custom TabControl View.
<Controls:EditableTextControl x:Name="PageTypeName"
Style="{StaticResource ResourceKey={x:Type Controls:EditableTextControl}}" Grid.Row="0" TabIndex="0"
Uid="0"
AutomationProperties.AutomationId="PageTypeNameTextBox"
AutomationProperties.Name="PageTypeName"
Visibility="{Binding ElementName=PageTabControl,Path=ShowPageType}">
<Controls:EditableTextControl.ContextMenu>
<ContextMenu x:Name="TabContextMenu">
<MenuItem Header="Rename Page Type" Command="{Binding Path=PlacementTarget.EnterEditMode, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
AutomationProperties.AutomationId="RenamePageTypeMenuItem"
AutomationProperties.Name="RenamePageType"/>
<MenuItem Header="Delete Page Type" Command="{Binding Path=PageTypeDeletedCommand}"
AutomationProperties.AutomationId="DeletePageTypeMenuItem"
AutomationProperties.Name="DeletePageType"/>
</ContextMenu>
</Controls:EditableTextControl.ContextMenu>
<Controls:EditableTextControl.Content>
<!--<Binding Path="CurrentPageTypeViewModel.Name" Mode="TwoWay"/>-->
<Binding ElementName="PageTabControl" Path="CurrentPageTypeName" Mode ="TwoWay"/>
</Controls:EditableTextControl.Content>
</Controls:EditableTextControl>
In the Content section I am binding to a Dependency Prop called CurrentPageTypeName. This Depedency prop is part of this custom Tab Control.
public static DependencyProperty CurrentPageTypeNameProperty = DependencyProperty.Register("CurrentPageTypeName", typeof(object), typeof(TabControlView));
public object CurrentPageTypeName
{
get { return GetValue(CurrentPageTypeNameProperty) as object; }
set { SetValue(CurrentPageTypeNameProperty, value); }
}
In another view, where I am using the custom TabControl I then bind my property, with the actual name value, to CurrentPageTypeName property as seen below:
<Views:TabControlView Grid.Row="0" Name="RunPageTabControl"
TabItemsSource="{Binding RunPageTypeViewModels}"
SelectedTab="{Binding Converter={StaticResource debugConverter}}"
CurrentPageTypeName="{Binding Path=RunPageName, Mode=TwoWay}"
TabContentTemplateSelector="{StaticResource tabItemTemplateSelector}"
SelectedIndex="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectedTabIndex}"
ShowPageType="Hidden" >
<!--<Views:TabControlView.TabContentTemplate>
<DataTemplate DataType="{x:Type ViewModels:RunPageTypeViewModel}">
<RunViews:RunPageTypeView/>
</DataTemplate>
</Views:TabControlView.TabContentTemplate>-->
</Views:TabControlView>
My problem is that nothing seems to be happening. It is grabbing its Content from the Itemsource, and not from my chained Dependency props. Is what I am trying even possible? If so, what have I done wrong.
Thanks for looking.
Unless I'm missing something this is definitely possible. Here is a simplified working example.
User control with a dependency property named TestValue, containing a TextBox bound to this property:
<UserControl x:Class="TestApp.TestControl" 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"
x:Name="TestControlName">
<Grid>
<TextBox Text="{Binding ElementName=TestControlName, Path=TestValue, Mode=TwoWay}"/>
</Grid>
</UserControl>
A different view using this user control, binding the above mentioned dependency property to something:
<Window x:Class="TestApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:TestApp="clr-namespace:TestApp" Title="MainWindow"
Height="350" Width="525">
<StackPanel>
<TestApp:TestControl TestValue="{Binding ElementName=SourceTextBox, Path=Text, Mode=TwoWay}" />
<TextBox Name="SourceTextBox" />
</StackPanel>
</Window>
It sounds that the issue is somewhere in the part of the code you have not posted (e.g. wrong name used in Content binding).
I think you already solved this yourself for the "SelectedIndex" property. Just do the same thing for the "CurrentPageType" property i.e. use RelativeSource

Resources