ToggleSwitch in UWP C# - c#

I have a little question. I'm trying to create an UWP C# application, and I'm using toggle switches in the project. If I toggle the status of the ToggleSwitch from the software very often, the memory usage increases very much. Why is this happening?
The ToggleSwitch represents a boolean using binding.
I have created example code:
XAML:
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleSwitch Header="Test" HorizontalAlignment="Left" Margin="213,27,0,0" VerticalAlignment="Top" IsOn="{Binding Path=TestBool, Mode=TwoWay}"/>
<CheckBox Content="Two-state CheckBox" Margin="108,162,0,806" IsChecked="{Binding Path=TestBool, Mode=TwoWay}"/>
<Button Content="Start!" HorizontalAlignment="Left" Margin="69,58,0,0" VerticalAlignment="Top" Click="Button_Click"/>
</Grid>
C#:
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using System.ComponentModel;
using System.Runtime.CompilerServices;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace App1
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
///
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
private bool testBool = false;
public bool TestBool { get { return testBool; } set { if (testBool != value) { testBool = value; OnPropertyChanged(); } } }
public MainPage()
{
DataContext = this;
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 10000; i++)
{
TestBool = !TestBool;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Do I do something that is not possible, what do I understand wrong?
When I replace the ToggleSwitch with a CheckBox, the memory usage increases much less.

It seems that a memory leak is occurring on your application, explaining why the GC failed to free it up.
Some considerations:
Why are you implementing INotifyPropertyChanged on the page level? You should implement it on a separate class. That class should hold the model and implement the INotifyPropertyChanged interface itself.
That class is commonly denominated as a ViewModel, and as the name suggests holds the model for a View and it knows how to communicate any changes onto it.
Generally most of the problems with memory leaks with Bindings, that have been reported happen generally when utilizing compiled bindings, {x:Bind ...}, but you are utilizing the traditional bindings. You've correctly performed binding onto a dependency property, such as IsOn and also implemented the INotifyPropertyChanged on the source object, so I don't think that exists something inherently wrong with how you are spinning up the binding process.
You are performing 100 bindings update in a row, so even if the CPU usage should be a bit lower, it is expected that while you are iterating over the loop you will have your CPU usage increased.
Ps: Since you have not marked your method as `async, you will be blocking the UI thread until your loop operation ends, which might be something that you could want to change.
A binding update, is certainly a process with a couple of actions in the middle for each that's why I'm "confident" that the CPU usage might not be a big issue.
I don't actually have a guess, but I would strongly suggest to refactor your code to have the ViewModel class implemented as an extra class, and observe if the memory leaks observed seize to exist.

Looks like your invoke is draining CPU.
My answer is you should think again about your app's architecture.

Related

WPF force xaml to re-fetch the property's value although the property's setter didn't change

I'm modifying an existing WPF project at work (I don't have much experience with WPF), and I have this property:
public Point WidgetMiddlePoint
{
get
{
return new PointByAppMonitorDPI(_middlePoint);
//return _middlePoint;
}
}
And this at the UI side:
<controls1:BorderWithTip.TipOffset>
<MultiBinding Converter="{StaticResource TipOffsetPositionConverter}">
<Binding Path="WidgetMiddlePoint" Delay="500" NotifyOnSourceUpdated="False" NotifyOnTargetUpdated="False"/>
<Binding ElementName="BorderWithTip" Path="ActualWidth" Delay="500" NotifyOnSourceUpdated="False" NotifyOnTargetUpdated="False"/>
</MultiBinding>
</controls1:BorderWithTip.TipOffset>
The TipOffsetPositionConverter performs some calculations based on the given parameters.
My problem is that the WidgetMiddlePoint value depends on the DPI of the monitor in which the app resides (the DPI isn't relevant for my question, it's just a use case for a factor that is taken into account only when calling the getter).
So what happens is that the UI takes the value from the getter and won't refresh that value unless I use the setter to set it to something else, and then 'notify'.
How can I configure the UI to re-get the value every time, even when it 'thinks' that the property's value hasn't changed? Or is it bad practice and not recommended?
If you want the the framework to call your getter, and consequently your converter, you should implement INotifyPropertyChanged and raise the PropertyChanged event in your view model.
You need to somehow determine when the DPI changes and then raise the event. The Convert method is only called whenever the framework is notified of a change of any of the data-bound properties (WidgetMiddlePoint and ActualWidth in this case).
There's a Window DpiChanged event you can use for this, along with INotifyPropertyChanged.
The code below shows how to do this. It has a RecalculateMiddlePoint method that creates a test Point that has the current DPI for both X and Y values, but clearly it should do the appropriate calculation.
If you create a WPF app, the code below binds the middle point to a label and hence shows the changing DPI on the main window as you drag it between screens. The code works in both .NET Framework 4.8 and .NET Core 3.1.
C#
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
// Hook the DpiChanged event
this.DpiChanged += Window_DpiChanged;
// Initialize our bound property
WidgetMiddlePoint = RecalculateMiddlePoint(VisualTreeHelper.GetDpi(this));
}
public event PropertyChangedEventHandler PropertyChanged;
private void Window_DpiChanged(object sender, DpiChangedEventArgs e)
{
Debug.WriteLine($"Old Scale: {e.OldDpi.DpiScaleX} New Scale: {e.NewDpi.DpiScaleX} " +
$"Old PPI: {e.OldDpi.PixelsPerInchX} New PPI: {e.NewDpi.PixelsPerInchX}");
// Recalculate _widgetMiddlePoint based on the values above and just set it
WidgetMiddlePoint = RecalculateMiddlePoint(e.NewDpi);
}
private Point RecalculateMiddlePoint(DpiScale newDpi)
{
// Recalculate based on the new DPI in here
// For testing we just create a 'Point' that has the PPI for X and Y values
return new Point(newDpi.PixelsPerInchX, newDpi.PixelsPerInchX);
//return new PointByAppMonitorDPI(_middlePoint); // Correct code????
}
private Point _middlePoint;
public Point WidgetMiddlePoint
{
get { return _middlePoint; }
set
{
_middlePoint = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(WidgetMiddlePoint)));
}
}
}
XAML
<Window x:Class="WpfApp9.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:WpfApp9"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Label Content="{Binding Path=WidgetMiddlePoint}" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="120"/>
</Grid>
</Window>
Add to an app.manifest:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
There is a PropertyChanged Add-in for Fody
https://github.com/Fody/PropertyChanged
It takes away a bit of the boilerplate for using INotifyPropertyChanged

Set language of the DocumentViewer to German (from code, not XAML)

I am trying to change the language of the DocumentViewer from default English to German but with no success.
Being new to WPF, I really struggle to do this.
IMPORTANT: DocumentViewer is created in code behind, in response to the menu item click, and then it is added as main window's Content.
I have tried doing the following, but it seems to do nothing:
myDocumentViewer.Language = System.Windows.Markup.XmlLanguage.GetLanguage("de-DE");
No changes are made, DocumentViewer keeps English.
Googling for proper usage of the Language property, I found nothing useful.
QUESTION:
How can I set the language of the DocumentViewer (created with code) to German?
What you are trying to accomplish can be done, but not very easily.
I'll start by pointing out that your test machine needs to have the appropriate language resources installed to permit DocumentViewer to show you tooltips etc. in German. In practice, this means that you'll need to have German (Germany) language pack installed on your computer. See Language Packs for details.
What comes below is what I know to the best of my understanding:
WPF does not quite have an in-built infrastructure, as far as I can tell, to dynamically adapt to changes in either Thread.CurrentThread.CurrentUILanguage or to changes in xml:lang(which is equivalent to FrameworkElement.Language property.
WPF controls primarily utilize xml:lang to determine their UI language (assuming that the corresponding UI resources are available), and it is up to the application developer to hook that up with Thread.CurrentThread.CurrentUILanguage if so desired. This in itself is not very hard to do using data-binding, like this:
<DocumentViewer Language="{Binding UILanguage, ConverterCulture={x:Static glob:CultureInfo.InvariantCulture}}" />
That still does not mean that the control thus data-bound would adapt its UI language to changes in Thread.CurrentThread.CurrentUILanguage. Every time you need the UI language to be changed, you need to recreate the control, remove the old control from the visual tree, and add the new one. Roughly, the code would look somewhat like this:
private void ChangeCulture()
{
string ietfLanguageTag = "de-DE";
var cultureInfo = CultureInfo.GetCultureInfo(ietfLanguageTag);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
UILanguage = ietfLanguageTag;
var parent = VisualTreeHelper.GetParent(_documentViewer) as Grid;
int index = parent.Children.IndexOf(_documentViewer);
parent.Children.Remove(_documentViewer);
_documentViewer = new DocumentViewer();
parent.Children.Add(_documentViewer);
}
The above snippet assumes that the visual parent of the DocumentViewer is a Grid, and it is backed by the variable _documentViewer.
Generally, the above solution is too simplistic and is not well suited for MVVM scenarios (which is often the case in WPF applications). You might have data bindings to the DocumentViewer instance, and creating new instances would require that those bindings be recreated in code (if, on the other hand, there happen to be no data-bindings involved, and all settings are set in code, then the above approach would just work, I think).
You can further improve this by creating a simple user control that encapsulates a DocumentViewer, along with any interesting bindings you might wish to preserve. Your control would look like this:
XAML:
<UserControl x:Class="LocalizedDocumentViewer.CultureAwareDocumentViewer"
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:glob="clr-namespace:System.Globalization;assembly=mscorlib"
xmlns:local="clr-namespace:LocalizedDocumentViewer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<DocumentViewer DataContext="{Binding}" Language="{Binding UILanguage, ConverterCulture={x:Static glob:CultureInfo.InvariantCulture}}" />
</Grid>
XAML.cs
using System.Windows.Controls;
namespace LocalizedDocumentViewer
{
public partial class CultureAwareDocumentViewer : UserControl
{
public CultureAwareDocumentViewer()
{
InitializeComponent();
}
}
}
Now, you can easily include this in your main application UI, like shown below. The XAML below includes a couple of additional UI elements (buttons and labels) that would help show a complete example:
MainWindow XAML:
<Window x:Class="DocViewerLoc.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:local="clr-namespace:DocViewerLoc"
xmlns:localizedDocumentViewer="clr-namespace:LocalizedDocumentViewer;assembly=LocalizedDocumentViewer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="DocumentViewer Culture Change Demo"
Width="525"
Height="350"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<Grid>
<!-- Row and Column Definitions -->
<!-- Define a small row on the top of the window to place buttons -->
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<!-- Controls -->
<Button Grid.Row="0"
Grid.Column="0"
Command="{Binding CultureChanger}"
CommandParameter="{Binding RelativeSource={RelativeSource Self},
Path=Content}">
en-us
</Button>
<Button Grid.Row="0"
Grid.Column="1"
Command="{Binding CultureChanger}"
CommandParameter="{Binding RelativeSource={RelativeSource Self},
Path=Content}">
de-DE
</Button>
<Label Grid.Row="0" Grid.Column="2"><-- Click on one of these buttons to change UI culture</Label>
<Grid Grid.Row="1" Grid.ColumnSpan="3">
<localizedDocumentViewer:CultureAwareDocumentViewer x:Name="_documentViewer" DataContext="{Binding}" />
</Grid>
</Grid>
The corresponding code-behind has a couple of dependency properties used to help communicate with the bindings in the above XAML.
MainWindow.xaml.cs
using System;
using System.Globalization;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace DocViewerLoc
{
public partial class MainWindow : Window
{
public MainWindow()
{
CultureChanger = new SimpleCommand(ChangeCulture);
InitializeComponent();
}
/// <summary>
/// ChangeCulture is called when one of the buttons with caption
/// 'en-us' or 'de-DE' is pressed.
/// </summary>
/// <param name="parameter">
/// A string containing the caption 'en-us' or 'de-DE'.
/// </param>
private void ChangeCulture(object parameter)
{
string ietfLanguageTag = parameter as string;
if (ietfLanguageTag == null) return;
var cultureInfo = CultureInfo.GetCultureInfo(ietfLanguageTag);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
// This will ensure that CultureAwareDocumentViewer's Language property
// binds to the updated value set here when it is instantiated next.
UILanguage = ietfLanguageTag;
// Remove the old instance of _documentViewer from the UI.
var parent = VisualTreeHelper.GetParent(_documentViewer) as Grid;
int index = parent.Children.IndexOf(_documentViewer);
parent.Children.Remove(_documentViewer);
// Create a new instance of CultureAwareDocumentViewer. This will
// use the updated value of UILanguage bind it to its Language (xml:lang)
// property, thus resulting in the appropriate language resources being
// loaded.
_documentViewer = new LocalizedDocumentViewer.CultureAwareDocumentViewer();
// Now, add the _documentViewer instance back to the UI tree.
parent.Children.Add(_documentViewer);
}
/// <summary>
/// ICommand used to bind to en-us and de-DE buttons in the UI
/// </summary>
#region CultureChange
public SimpleCommand CultureChanger
{
get { return (SimpleCommand)GetValue(CultureChangerProperty); }
set { SetValue(CultureChangerProperty, value); }
}
// Using a DependencyProperty as the backing store for CultureChanger. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CultureChangerProperty =
DependencyProperty.Register("CultureChanger", typeof(SimpleCommand), typeof(MainWindow), new PropertyMetadata(default(SimpleCommand)));
#endregion
/// <summary>
/// UILanguage property used to bind to the FrameworkElement.Language (xml:lang) property
/// in the DocumentViewer object within the CultureAwareDocumentViewer control.
/// </summary>
#region UILanguage
public string UILanguage
{
get { return (string)GetValue(UILanguageProperty); }
set { SetValue(UILanguageProperty, value); }
}
// Using a DependencyProperty as the backing store for UILanguage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UILanguageProperty =
DependencyProperty.Register("UILanguage", typeof(string), typeof(MainWindow), new PropertyMetadata(Thread.CurrentThread.CurrentUICulture.IetfLanguageTag));
#endregion
}
/// <summary>
/// Simple implementation of the ICommand interface that delegates
/// Execute() to an Action<object>.
/// </summary>
public class SimpleCommand : ICommand
{
#pragma warning disable 67
public event EventHandler CanExecuteChanged;
#pragma warning restore 67
public SimpleCommand(Action<object> handler)
{
_handler = handler;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_handler?.Invoke(parameter);
}
private Action<object> _handler;
}
}
The below screenshot show the resulting application UI. Note that the resources in DocumentViewer would switch between English and German, but the rest of the UI would not (because we did not try to localize our application!).
Application showing en-us resources in DocumentViewer:
Application showing de-DE resources in DocumentViewer:
AFAIK you are setting it correctly.
I do not have experience with DocumentViewer, but setting CurrentUICulture does not translate. Setting CurrentUICulture selects between resources that you have in your application for different languages. See https://stackoverflow.com/a/1142840/5569663 for an example. I assume that Language of a DocumentViewer is the same.
Unluckily setting the Language property of your DocumentViewer won't work. The reason of this issue is related to the PresentationUI assembly (it is a standard one), which contains resources which affect DocumentViewer.
The main point is that tooltips and labels are hardcoded in these resources and they are only in english (at least considering .NET 4).
For example, this is how the DocumentViewer's print button is defined (in the themes/generic.baml resource):
<Button Name="PrintButton" x:Uid="Button_14" ToolTip="Print (Ctrl+P)" ToolTipService.ShowOnDisabled="True" Width="24px" Padding="2,2,2,2" Margin="2,2,2,2" VerticalAlignment="Center" Command="ApplicationCommands.Print" IsTabStop="True" TabIndex="0" Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type ui:PresentationUIStyleResources}, ResourceId=PUIDocumentViewerButtonStyle}}" Background="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type ui:PresentationUIStyleResources}, ResourceId=PUIDocumentViewerPrintButton}}" CommandTarget="{Binding Path=TemplatedParent, RelativeSource={RelativeSource TemplatedParent}}" />
As you can see is defined as "Print (Ctrl+P)". You will find the same situation for other labels which you would localize. Even if x:Uid properties are defined LocBaml won't work since PresentationUI is not a satellite assembly.
So a first solution could be: write your own DocumentViewer style and you can use the language that you prefer. The problem is that inside a DocumentViewer there is a control named FindToolBar. It is declared as internal, so probably it would be hard to redefine its style.
Then I propose an alternative solution to you. My idea is based on the fact that localizable children of a DocumentViewer have a name (you can use ILSpy to establish it).
So you need just to extend DocumentViewer in this way:
public class LocalizedDocumentViewer : DocumentViewer
{
public LocalizedDocumentViewer()
{
Loaded += new RoutedEventHandler(OnLoaded);
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Button button = FindChild<Button>(this, "PrintButton");
button.ToolTip = Properties.Resources.PrintToolTip;
button = FindChild<Button>(this, "CopyButton");
button.ToolTip = Properties.Resources.CopyToolTip;
button = FindChild<Button>(this, "FindPreviousButton");
button.ToolTip = Properties.Resources.FindPreviousToolTip;
button = FindChild<Button>(this, "FindNextButton");
button.ToolTip = Properties.Resources.FindNextToolTip;
Label label = FindChild<Label>(this, "FindTextLabel");
label.Content = Properties.Resources.FindTextLabel;
/* and so on... */
}
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
/* see the link for the code */
}
}
You can find the code of FindChild method here (take a look to CrimsonX's answer).
I know, it is a unelegant solution. I do not like it too. But I guess it is fast and it allows you to preserve the default style look.

Cannot find GalaSoft.MvvmLight.CommandWpf namespace in MVVM Light 5.2.0.37222

I just tried to update one of my WPF projects from MVVM Light 4.2.30 to 5.2. After that I noticed that my RelayCommands do not fire their CanExecute methods anymore.
After a quick search I found several articles that explain the problem and suggest using the GalaSoft.MvvmLight.CommandWpf namespace instead of GalaSoft.MvvmLight.Command. However I cannot find the GalaSoft.MvvmLight.CommandWpf namespace. When I look at the GalaSoft.MvvMGalaSoft.MvvmLight.dll in Visual Studio's 'Object Browser' then I also cannot find this namespace.
As it seems nobody else but I have that problem - any ideas what I'm doing wrong?
Update:
I've created a small example project that shows how I currently use the RelayCommands with their CanExecute methods in Version 4.2.30 of MVVM light:
public class ViewModel : ViewModelBase
{
private bool _isReadOnly = false;
public ViewModel ()
{
this.DoSomethingCommand = new RelayCommand(DoSomething, CanDoSomething);
}
public bool IsReadOnly
{
get
{
return _isReadOnly;
}
set
{
_isReadOnly = value;
this.RaisePropertyChanged("IsReadOnly");
// With MVVMLight 4.2.30.23246 I did not need to call the RaiseCanExecuteChanged on any of my RelayCommands
// DoSomethingCommand.RaiseCanExecuteChanged();
}
}
public RelayCommand DoSomethingCommand { get; set; }
private bool CanDoSomething()
{
return !this.IsReadOnly;
}
private void DoSomething()
{
MessageBox.Show("Let's break the MVVM idea...");
}
}
The XAML code of the view is:
<Window x:Class="MVVMLight5.2CanExecuteTest.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:MVVMLight5._2CanExecuteTest"
mc:Ignorable="d"
Title="Test" Height="150" Width="200">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<CheckBox HorizontalAlignment="Center" Grid.Row="0" Grid.Column="0" Content="Is read only" IsChecked="{Binding IsReadOnly, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Row="1" Grid.Column="0" Content="Break me" Command="{Binding DoSomethingCommand}"/>
</Grid>
My target is that if I have a button in the View that uses the 'DoSomethingCommand' as a Command then this button should become disabled when my IsReadOnly property is turned to false.
When using MVVM light 4.2.30 then this works without any additional so far but in MVVM light 5.2 I need to add the DoSomethingCommand.RaiseCanExecuteChanged(); to make the button go disabled in view.
Can I somehow get the old behavior with the new MVVM light framework?
TL;DR: Ensure you have a reference to GalaSoft.MvvmLight.Platform, and then you can use the CommandWpf namespace. Or, double check that you're linking against the .NET 4.5 version of MVVM Light; I think you're linking to the .NET 4.0 version
Laurent (the author of MVVMLight) has actually addressed all of this on his blog. On why he created a different namespace:
In the portable class library version of MVVM Light, there is no CommandManager.
...
The first obvious solution I considered was this: Move the RelayCommand out of the GalaSoft.MvvmLight.dll assembly and into the GalaSoft.MvvmLight.Platform.dll. Because this DLL is not a PCL (hence the “platform” name), it can contains platform-specific elements. If I move the RelayCommand there, it will work with the CommandManager just like before. However, it will not be available for PCL-based assemblies anymore, which is a real issue. That’s why I decided against this decision.
Instead, I decided to duplicate the RelayCommand class. If you check the WPF source code, you will see that this class is linked in the GalaSoft.MvvmLight assembly, and also in the GalaSoft.MvvmLight.Platform assembly. This is the same class! To avoid conflicts however, the one in the Platform assembly is declared in the GalaSoft.MvvmLight.CommandWpf namespace.
With that context, I'm still a little confused as to why things are the way they are. When I disassemble the libraries published on Nuget, here's what I see:
MvvmLightLibs.5.2.0.0\lib\net40 (assembly version 5.2.0.37222):
namespace GalaSoft.MvvmLight.Command
{
/// <omitted, see below>
public class RelayCommand<T> : ICommand
{
private readonly WeakAction<T> _execute;
private readonly WeakFunc<T, bool> _canExecute;
/// <summary>
/// Occurs when changes occur that affect whether the command should execute.
///
/// </summary>
public event EventHandler CanExecuteChanged;
MvvmLightLibs.5.2.0.0\lib\net45 (assembly version 5.2.0.37223):
namespace GalaSoft.MvvmLight.Command
{
/// <omitted, see below>
public class RelayCommand<T> : ICommand
{
private readonly WeakAction<T> _execute;
private readonly WeakFunc<T, bool> _canExecute;
/// <summary>
/// Occurs when changes occur that affect whether the command should execute.
///
/// </summary>
public event EventHandler CanExecuteChanged
{
add
{
if (this._canExecute == null)
return;
CommandManager.RequerySuggested += value;
}
remove
{
if (this._canExecute == null)
return;
CommandManager.RequerySuggested -= value;
}
}
Note that the 4.5 version of CanExecuteChanged uses the CommandManager class which is why you never needed to call RaiseCanExecuteChanged(). The 4.0 version is just a regular event, so you need to call RaiseCanExecuteChanged() yourself.
Also note the assembly versions are different between the two, and since in your question you say you're using 5.2.0.37222, I'd say you're using the 4.0 library. This is why I think just referencing the 4.5 version will fix it.
Unfortunately I can't figure out why the two versions are different by looking at the source code. None of those three constants are defined in the .NET 4.0 version of the project, so why doesn't it generate the branch with the CommandManager?

Simple string property won't seem to bind in XAML GUI

I have a Page where I want to display a string property in a Label.
This is my code, but nothing will appear in the label.
This is my .xaml
<Page x:Class="MyProject.PageOne"
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"
Title="PageOne"
Name="pageOne>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>
<Label Grid.Column="0" Content="{Binding ElementName=pageOne, Path=aStr}" FontWeight="Normal" FontSize="43" HorizontalAlignment="Left" Margin="0,00,0,0" VerticalAlignment="Center" Foreground="White"/>
</Grid>
</Page>
And this is my .cs code
public partial class PageOne: Page, IPageInterface
{
public String aStr{get;set;}
public PageOne()
{
InitializeComponent();
}
public void Start()
{
aStr = "Test";
}
}
The only thing really wrong with the code you posted, in terms of the problem you describe, is that you have not implemented some way for property change notifications to occur. Because the aStr property is not set to the new value until after the Label content has been initially set, without a way to receive notification, the framework has no way to know it needs to update the Label content.
In WPF the two main ways this is typically done (indeed, AFAIK the only two fully supported ways) are to create DependencyProperty instances, or to implement INotifyPropertyChanged. Either will work fine.
Here is an example of how your code should look with INotifyPropertyChanged implemented:
public partial class PageOne : Page, IPageInterface, INotifyPropertyChanged
{
private string _astr;
public String aStr
{
get { return _astr; }
set { _astr = value; OnPropertyChanged(); }
}
public Page1()
{
InitializeComponent();
}
public void Start()
{
aStr = "Test";
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Implementing the interface involves a couple of simple steps:
Declare the event named PropertyChanged
Any time a property is changed, raise that event, passing the name of the property that's changing.
Note that to do this, you can't use auto-implemented properties. You need to implement each property yourself, with a backing field, and a call to a method that will raise the property.
.NET offers the convenient [CallerMemberName] attribute, which I show here. So in the setter method for your aStr property, after setting the backing field's value, you simply call the method without any parameters, and the runtime automatically fills in the correct property name for you.
Now, the code you posted has some other problems as well, in the XAML. First it won't compile because you left out a " character, and because you've got an extra </Grid> closing tag.
One other possible problem, though it's not possible to know for sure since we are missing the full context of how you display this Page object, is that the text's color is white. If you're putting the Label instance on a white background, then of course you won't be able to see the text, even if it were set correctly.
I note that commenter Franck has suggested that you should set the DataContext. The truth is, given the code you posted this is actually not necessary, and doing so wouldn't actually fix the problem you are having.
But if you do fix the underlying notification issue, then his suggestions are an alternative way that you can achieve the binding. By setting the DataContext to the object containing the property (here, your PageOne class), then when you are binding you can just specify the property name alone, without having to include the ElementName at all, and without having to use the Path= with the property name. You may find this technique more convenient, at least some of the time.
In the future, please take the time to provide a good, minimal, complete code example that reliably reproduces the problem. You are more likely to get an answer that way, and you will ensure that any answer you do get is as good as it can be.

ReadOnlyCheckBox ControlTemplate

I've been trying to create my generic ReadOnlyCheckBox style/template but I'm having a problem with the binding to the data. In the example here:
A read-only CheckBox in C# WPF
you bind directly to the data from the ControlTemplate definition, but of course this is not really what I want, as I want to be able to declare the new checkbox something like this:
<CheckBox x:Name="uiComboBox" Content="Does not set the backing property, but responds to it."
Style="{StaticResource ReadOnlyCheckBoxStyle}" IsChecked="{Binding MyBoolean}" Click="uiComboBox_Click"/>
Except of course when I do this and then set the event trigger on the bullet to be a TemplateBinding of IsChecked I have exactly what I started with! I guess I don't understand why setting the binding directly in the bullet is different from setting IsChecked and then binding to that, isn't the TemplateBinding just a way of referencing what is set in the properties of the control being created? How is the Click triggering the UI update even tho the data does not get updated? Is there a trigger for Click I can override to stop the update?
I got all the DictionaryResource stuff working fine so I am happy with that, cheers for the pointer.
The other thing I was curious about was if it is possible to reduce my Control/Style template by using the BasedOn parameter in the style, then I would only override the things I actually need to change rather than declaring a lot of stuff that I think is part of the standard template anyway. I might have a play with this.
Cheers
ed
The problem you're running into is that you're trying to use DataBinding where you shouldn't.
I disagree with other answers you've been getting in the link you've posted. While the ControlTemplate solutions appear neat and tidy, they don't get at the heart of your problem, which is, you're trying to use a single control (a CheckBox) to do two different things: show state (e.g. checked/unchecked) and perform logic (remote device, etc.).
ControlTemplates are designed to change the way a control appears, but not behaves. You're trying to change behavior, not appearance. See "What Is a ControlTemplate?" here for more details
To do that, you're going to have to store some forego the standard binding and handle some events:
XAML:
<Window x:Class="WPFCheckBoxClickTester.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<CheckBox x:Name="foo" Checked="foo_Checked"></CheckBox>
</Grid>
</Window>
Code-Behind:
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace WPFCheckBoxClickTester
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
private BackgroundWorker _worker = new BackgroundWorker();
public Window1()
{
InitializeComponent();
foo.IsThreeState = false;
this._worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
this._worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
}
private void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
foo.IsChecked = true;
foo.IsEnabled = true;
return;
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(500);
return;
}
private void foo_Checked(object sender, RoutedEventArgs e)
{
if( foo.IsChecked == true && this.foo.IsEnabled )
{
this.foo.IsChecked = false;
this.foo.IsEnabled = false;
this._worker.RunWorkerAsync();
}
return;
}
}
}
The above code example gives you the ability to execute some code async via BackgroundWorker -- I've put in a Thread.Sleep as a placeholder. I'm also using foo.IsEnabled as a sentinel value in foo_Checked, but you may need some extra state here to handle all of your cases (for instance, going from Checked to Unchecked).

Categories