I have the following class (abreviated for simplicity). The app it multi-threaded so the Set and Get are a bit more complicated but should be ok.
namespace News.RSS
{
public class FeedEngine : DependencyObject
{
public static readonly DependencyProperty _processing = DependencyProperty.Register("Processing", typeof(bool), typeof(FeedEngine), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender));
public bool Processing
{
get
{
return (bool)this.Dispatcher.Invoke(
DispatcherPriority.Normal, (DispatcherOperationCallback)delegate { return GetValue(_processing); }, Processing);
}
set
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(SendOrPostCallback)delegate { SetValue(_processing, value); },
value);
}
}
public void Poll()
{
while (Running)
{
Processing = true;
//Do my work to read the data feed from remote source
Processing = false;
Thread.Sleep(PollRate);
}
//
}
}
}
Next I have my main form as the following:
<Window x:Class="News.Main"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="clr-namespace:News.Converters"
xmlns:local="clr-namespace:News.Lookup"
xmlns:rss="clr-namespace:News.RSS"
Title="News" Height="521" Width="927" Initialized="Window_Initialized" Closing="Window_Closing" >
<Window.Resources>
<ResourceDictionary>
<converter:BooleanConverter x:Key="boolConverter" />
<converter:ArithmeticConverter x:Key="arithConverter" />
...
</ResourceDictionary>
</Window.Resources>
<DockPanel Name="dockPanel1" SnapsToDevicePixels="False" >
<ToolBarPanel Height="37" Name="toolBarPanel" Orientation="Horizontal" DockPanel.Dock="Top" >
<ToolBarPanel.Children>
<Button DataContext="{DynamicResource FeedEngine}" HorizontalAlignment="Right" Name="btnSearch" ToolTip="Search" Click="btnSearch_Click" IsEnabled="{Binding Path=Processing, Converter={StaticResource boolConverter}}">
<Image Width="32" Height="32" Name="imgSearch" Source="{Resx ResxName=News.Properties.Resources, Key=Search}" />
</Button>
...
</DockPanel>
</Window>
As you can see I set the DataContext to FeedEngine and Bind IsEnabled to Processing. I have also tested the boolConverter separately and it functions (just applies ! (Not) to a bool).
Here is my Main window code behind in case it helps to debug.
namespace News
{
/// <summary>
/// Interaction logic for Main.xaml
/// </summary>
public partial class Main : Window
{
public FeedEngine _engine;
List<NewsItemControl> _newsItems = new List<NewsItemControl>();
Thread _pollingThread;
public Main()
{
InitializeComponent();
this.Show();
}
private void Window_Initialized(object sender, EventArgs e)
{
// Load current Feed data.
_engine = new FeedEngine();
ThreadStart start = new ThreadStart(_engine.Poll);
_pollingThread = new Thread(start);
_pollingThread.Start();
}
}
}
Hope someone can see where I missed a step.
Thanks.
The most obvious issue is that you're not using DependencyProperty correctly. For any DependencyProperty the wrapper property should stick to the boilerplate calls to GetValue and SetValue and never contain other code. The primary reason for this is that some usage scenarios (including XAML) only use the wrapper property as an indicator that the property can be accessed (try removing it) and the actual get/set instead make direct calls to GetValue/SetValue. Any additional actions that would normally be done in a setter should be put into a PropertyChanged handler attached as an additional parameter in the Register call.
It looks like what you want is to set Processing from your background thread and read it from the binding in the UI. Since a DependencyProperty is owned by its creating thread (in this and most cases the UI thread) you'll need that Dispatcher.BeginInvoke code when setting the value but it should be moved somewhere else - like into Poll(). The same would be true if you were using INotifyPropertyChanged instead of a DependencyObject, which you could do based on the code here.
Related
I have a WPF window which use heavy library and takes time to be fully rendered.
This library is in an UserControl.
This window is open by a context menu command in the parent window.
Using MVVM pattern, I need to get the DialogResult of this new window when closing to access the viewmodel.
When clicking the context menu item to open this new window, the context menu stays open until the instanciation of the new window will be done.
What can I do to close the context menu before open this window?
Here is the code refactored with the help of BionicCode:
MAIN WINDOW XAML
<Image Source="{Binding ImagePath}" Height="100" Width="100">
<Image.ContextMenu>
<ContextMenu>
<MenuItem Header="Open Window"
Command="{x:Static local:MainWindow.ShowMyDialogCommand}"
CommandTarget="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget}"
/>
</ContextMenu>
</Image.ContextMenu>
</Image>
MAIN WINDOW
public partial class MainWindow : Window
{
public static RoutedCommand ShowMyDialogCommand { get; } = new RoutedCommand("ShowMyDialogCommand", typeof(MainWindow));
private readonly MainVM myMainVM;
public MainWindow()
{
InitializeComponent();
myMainVM = new MainVM();
DataContext = myMainVM;
var showMyDialogCommandBinding = new CommandBinding(ShowMyDialogCommand, ExecuteShowMyDialogCommand, CanExecuteShowMyDialogCommand);
this.CommandBindings.Add(showMyDialogCommandBinding);
}
private void CanExecuteShowMyDialogCommand(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = true;
private void ExecuteShowMyDialogCommand(object sender, ExecutedRoutedEventArgs e)
{
ViewerVM vm = new ViewerVM();
var okDialog = new OkDialog()
{
Title = "Viewer Dialog",
DataContext = vm
};
bool? dialogResult = okDialog.ShowDialog();
if (dialogResult == true)
{
this.myMainVM.HandleData(vm);
}
}
}
MAIN VM
public class MainVM : ObservableObject
{
private string myImagePath;
public MainVM()
{
myImagePath = "flower.jpg";
}
public string ImagePath
{
get { return myImagePath; }
set
{
if (myImagePath == value) return;
myImagePath = value;
OnPropertyChanged(nameof(ImagePath));
}
}
public void HandleData(ViewerVM viewModel)
{
//Do stuffs
}
}
NEW WINDOW XAML
<Window.Template>
<ControlTemplate TargetType="Window">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<!-- Dynamic content row -->
<RowDefinition Height="Auto" />
<!-- Static content row (ok and cancel buttons etc.) -->
</Grid.RowDefinitions>
<!-- Dynamic content -->
<ContentPresenter Grid.Row="0" />
<!-- Static content -->
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="Ok" IsDefault="True" Command="{x:Static local:OkDialog.OkCommand}" />
<Button Content="Cancel" IsCancel="True" />
</StackPanel>
</Grid>
</ControlTemplate>
</Window.Template>
NEW WINDOW
public partial class OkDialog : Window
{
public static RoutedCommand OkCommand { get; } = new RoutedCommand("OkCommand", typeof(MainWindow));
public OkDialog()
{
InitializeComponent();
var okCommandBinding = new CommandBinding(OkDialog.OkCommand, ExecuteOkCommand, CanExecuteOkCommand);
this.CommandBindings.Add(okCommandBinding);
this.DataContextChanged += OnDataContextChanged;
}
// If there is no explicit Content set, use the DataContext
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) => this.Content = e.NewValue;
private void CanExecuteOkCommand(object sender, CanExecuteRoutedEventArgs e)
=> e.CanExecute = (this.DataContext as IOkDialogVM).CanExecuteOkCommand() ? true : false;
private void ExecuteOkCommand(object sender, ExecutedRoutedEventArgs e)
=> this.DialogResult = true;
}
interface IOkDialogVM
{
bool CanExecuteOkCommand();
}
UserControl
<UserControl x:Class="ContextMenuTest.Viewer"
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:ddes="http://schemas.devdept.com/winfx/2008/xaml/control"
xmlns:ddgr="http://schemas.devdept.com/winfx/2008/xaml/graphics"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<ddes:Design x:Name="myDesigner" Height="300" Width="300">
<ddes:Design.Viewports>
<ddes:Viewport>
<ddes:Viewport.Background>
<ddgr:BackgroundSettings StyleMode="Solid" TopColor="White"/>
</ddes:Viewport.Background>
</ddes:Viewport>
</ddes:Design.Viewports>
</ddes:Design>
</Grid>
</UserControl>
public partial class Viewer : UserControl
{
public Viewer()
{
InitializeComponent();
}
}
public class ViewerVM : ObservableObject, IOkDialogVM
{
public bool CanExecuteOkCommand() => true;
}
App.xaml
<Application.Resources>
<DataTemplate DataType="{x:Type local:ViewerVM}">
<local:Viewer/>
</DataTemplate>
</Application.Resources>
Your current code breaks the MVVM design pattern. This is because you are managing views in your View Model. The view model class has no idea that the view will show a dialog. It therefore doesn't participate in any dialog flow.
You control the dialog completely in the View. You show it and you close it without any dependency on a view model class.
When you make use of the Button.IsCancel property the Window will close itself without the need to attach any event handler or close commands to this Button.
Setting the Window.DialogResult will always close the Window and let the Window.ShowDialog return the Window.DialogResult. You only need to attach an event handler to set the Window.DialogResult to true or false.
Window will take care of the rest. It's as easy as it can get. No View Model needed.
To show a dialog in an MVVM application, you can follow the below examples in the sections: MVVM compliant dialog flow and Advanced MVVM compliant dialog flow.
To fix the loading experience, you shouldn't create any views in the constructor. Only do some light work in the constructor so that the constructor can return fast.
As a general rule, you should always avoid creating controls in your code-behind to add them manually to the visual tree. This is done in XAML, which wouldn't cause your current issue in the first place.
If you really need to do it your way, chose to create the views either in the FrameworkElement.Loaded event or override the FrameworkElement.OnApplyTemplate method.
Because of the heavy load, I suggest to move your code to the Loaded event handler.
It's unclear what your DesignView constructor is exactly doing. In case you have shown the complete constructor and the timing of the call of the following line
devDept.LicenseManager.Unlock(typeof(devDept.Eyeshot.Workspace), "mykey");
doesn't matter or can be deferred, you should move this line to the Loaded event handler too. Just in case LicenseManager.Unlock is the blocking piece.
public partial class PartEditView : UserControl
{
private DesignerView myDesignerView;
public PartEditView()
{
InitializeComponent();
// Follow this pattern to unlock the DesignerView.
this.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
this.myDesignerView = new DesignerView();
this.myDesignerContainer.Children.Add(myDesignerView);
}
}
MVVM compliant dialog flow
The idea is simple, your View is responsible to show the dialog. Data is displayed/collected by binding elements to a dedicated view model of the dialog. After the dialog was closed, the View can interact with the View Model to pass over the data. In most scenarios the view model of the dialog knows how to handle the data (for example how to use the Model to persist data).
MainWindowViewModel.cs
The view model class has no idea that the view will show a dialog.
It doesn't participate in any dialog flow.
If the view model must handle the data collected by a dialog, the responsible view can pass the data to the view model.
class MainWindowViewModel : INotifyPropertyChanged
{
// Such a public method is one possible way to allow the view to pass data
// to this instance. Simply use the common means to send data from View to View Model.
public void HandleData(MyDialogViewModel viewModel)
{
}
}
MainWindow.xaml.cs
partial class MainWindow : Window
{
public static RoutedCommand ShowMyDialogCommand { get; } = new RoutedCommand("ShowMyDialogCommand", typeof(MainWindow));
private MainWindowViewModel MainWindowViewModel { get; }
public MainWindow()
{
InitializeComponent();
this.MainWindowViewModel = new MainWindowViewModel();
this.DataContext = this.MainWindowViewModel;
var showMyDialogCommandBinding = new CommandBinding(ShowMyDialogCommand, ExecuteShowMyDialogCommand, CanExecuteShowMyDialogCommand);
this.CommandBindings.Add(showMyDialogCommandBinding);
}
private void CanExecuteShowMyDialogCommand(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = true;
private void ExecuteShowMyDialogCommand(object sender, ExecutedRoutedEventArgs e)
{
var myDialogViewModel = new MyDialogViewModel();
var myDialog = new MyDialog()
{
Content = "I'm a dialog",
DataContext = myDialogViewModel
};
bool? dialogResult = myDialog.ShowDialog();
// Do something when the user has closed the dialog e.g. using the 'OK' button
if (dialogResult == true)
{
// Pass the dialog data (if it has some) to the view model class
// for further processing. The data is stored via data binding in the
// myDialogViewModel (the DataContext of the dialog).
// Depending on the context of the dialog, the dialog's view model
// knows what to do with the data (e.g. save it to a database using the Model).
this.MainWindowViewModel.HandleDialogData(myDialogViewModel);
}
}
}
MainWindow.xaml
Because the ContextMenu will have its own visual tree (it uses a Popup to display content), the routed command must be executed in the visual tree of the parent Window. For this reason we must explicitly set the MenuItem.CommandTarget property to point to the visual tree outside of the ContextMenu. The CommandTarget will therefore point to the ContextMenu.PlacementTarget (which is the Image in the example). The Image is an element of the Window visual tree where the CommandBinding is defined.
This is only necessary when the routed command is used inside a Popup (for example ContextMenu).
Otherwise setting the CommandTarget is not necessary.
<Window>
<StackPanel>
<!-- CommandTarget is not needed when the ICommandSource is part of the parent Window's visual tree -->
<Button Command="{x:Static local:MainWindow.ShowMyDialogCommand}" />
<Image>
<Image.ContextMenu>
<ContextMenu>
<!-- Visual tree is different from the Window (due to the Popup).
Set CommandTarget to allow the command to traverse the visual tree
of the MainWindow to reach to the CommandBindng (defined by the MainWindow) -->
<MenuItem Header="Open Window"
Command="{x:Static local:MainWindow.ShowMyDialogCommand}"
CommandTarget="{Binding RelativeSource={RelativeSource
AncestorType=ContextMenu}, Path=PlacementTarget}" />
</ContextMenu>
</Image.ContextMenu>
</Image>
</StackPanel>
</Window>
MyDialog.xaml.cs
partial class MyDialog : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OkButton_Click(object sender, RoutedEventArgs e)
{
// Setting the DialogResult will automatically close the Window
// and return the DialogResult value.
this.DialogResult = true;
}
}
MyDialog.xaml
It's important to set Button.IsCancel to true for the "Cancel" button.
This allows the Window to close itself automatically.
Closing the Window in case of the "Ok" button being clicked is achieved by setting the Window.DialogResult property from a Button.Click handler (or RoutedCommand). Window will always close itself when Window.DialogResult is set.
<Window>
<Grid>
<Grid.RowDefinitions>
<RowDefinition /> <!-- Content row -->
<RowDefinition Height="Auto" /> <!-- Dialog button row -->
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Text="I'm a custom dialog" />
<StackPanel Grid.Row="1"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Content="Ok"
IsDefault="True"
Click="OkButton_Click"/>
<Button Content="Cancel"
IsCancel="True" />
</StackPanel>
</Grid>
</Window>
Advanced MVVM compliant dialog flow
A more advanced version will make use of the fact that the Window is a ContentControl. This means we can define the content based on a data model (like the above MyDialogViewModel) and load the associated view by defining a DataTemplate, preferably implicit (without the x:Key directive defined). This makes the dialog highly reusable and easy to deal with in an MVVM context.
The following example defines a dialog that only knows how to handle an "Ok" and "Cancel" button. But through data templating the same class can show all kind of views.
IOkDialogViewModel.cs
interface IOkDialogViewModel
{
bool CanExecuteOkCommand();
}
OkDialogViewModel.cs
Example data model that is mapped to a dedicated view via a DataTemplate
that makes the content of the dialog.
// Consider to implement INotifyDataErrorInfo
public class OkDialogViewModel : IOkDialogViewModel, INotifyPropertyChanged
{
public string SomeText { get; set; }
public event PropertyChangedEventHandler? PropertyChanged;
public bool CanExecuteOkCommand() => this.SomeText.StartsWith("#");
}
OkDialog.xaml.cs
public partial class OkDialog : Window
{
public static RoutedCommand OkCommand { get; } = new RoutedCommand("OkCommand", typeof(MainWindow));
public OkDialog()
{
InitializeComponent();
var okCommandBinding = new CommandBinding(OkDialog.OkCommand, ExecuteOkCommand, CanExecuteOkCommand);
this.CommandBindings.Add(okCommandBinding);
this.DataContextChanged += OnDataContextChanged;
}
// If there is no explicit Content set, use the DataContext
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) => this.Content ??= e.NewValue;
private void CanExecuteOkCommand(object sender, CanExecuteRoutedEventArgs e)
=> e.CanExecute = (this.DataContext as IOkDialogViewModel)?.CanExecuteOkCommand() ?? true;
private void ExecuteOkCommand(object sender, ExecutedRoutedEventArgs e)
=> this.DialogResult = true;
}
OkDialog.xaml
Now hardcode the default content (the "Ok" and "Close" buttons) into the Window.Template. This will make the static content.
The dynamic content is implicitly created by the client who defined a DataTemplate for the Window.Content.
<Window>
<Window.Template>
<ControlTemplate TargetType="Window">
<Grid>
<Grid.RowDefinitions>
<RowDefinition /> <!-- Dynamic content row -->
<RowDefinition Height="Auto" /> <!-- Static content row (ok and cancel buttons etc.) -->
</Grid.RowDefinitions>
<!-- Dynamic content -->
<ContentPresenter Grid.Row="0" />
<!-- Static content -->
<StackPanel Grid.Row="1"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Content="Ok"
IsDefault="True"
Command="{x:Static local:OkDialog.OkCommand}" />
<Button Content="Cancel"
IsCancel="True" />
</StackPanel>
</Grid>
</ControlTemplate>
</Window.Template>
</Window>
App.xaml
Define a DataTemplate to crate the particular dialog view that is associated with the OkDialogViewModel.
<Application>
<Application.Resources>
<DataTemplate DataType="{x:Type local:OkDialogViewModel}">
<TextBox Text="{Binding SomeText}" />
</DataTemplate>
</Application.Resources>
</Application>
MainWindow.xaml.cs
partial class MainWindow : Window
{
public static RoutedCommand ShowMyDialogCommand { get; } = new RoutedCommand("ShowMyDialogCommand", typeof(MainWindow));
private MainWindowViewModel MainWindowViewModel { get; }
public MainWindow()
{
InitializeComponent();
this.MainWindowViewModel = new MainWindowViewModel();
this.DataContext = this.MainWindowViewModel;
var showMyDialogCommandBinding = new CommandBinding(ShowMyDialogCommand, ExecuteShowMyDialogCommand, CanExecuteShowMyDialogCommand);
this.CommandBindings.Add(showMyDialogCommandBinding);
}
private void CanExecuteShowMyDialogCommand(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = true;
private void ExecuteShowMyDialogCommand(object sender, ExecutedRoutedEventArgs e)
{
// Because the text doesn't start with '#', the OK button will be disabled later,
// until the user fixes the input in the TextBox.
var dialogViewModel = new OkDialogViewModel() { SomeText = "Just some text" };
var okDialog = new OkDialog()
{
Title = "I'm an Ok dialog",
DataContext = dialogViewModel
};
bool? dialogResult = okDialog.ShowDialog();
// Do something when the user has closed the dialog e.g. using the 'OK' button
if (dialogResult == true)
{
// Pass the dialog data (if it has some) to the view model class
// for further processing. The data is stored via data binding in the
// DataContext/Content of the dialog.
// Depending on the context of the dialog, the dialog's view model
// knows what to do with the data (e.g. save it to a database using the Model).
this.MainWindowViewModel.HandleData(dialogViewModel);
}
}
}
MainWindow.xaml
Because the ContextMenu will have its own visual tree (it uses a Popup to display content), the routed command must be executed in the visual tree of the parent Window. For this reason we must explicitly set the MenuItem.CommandTarget property to point to the visual tree outside of the ContextMenu. The CommandTarget will therefore point to the ContextMenu.PlacementTarget (which is the Image in the example). The Image is an element of the Window visual tree where the CommandBinding is defined.
This is only necessary when the routed command is used inside a Popup (for example ContextMenu).
Otherwise setting the CommandTarget is not necessary.
<Window>
<StackPanel>
<!-- CommandTarget is not needed when the ICommandSource is part of the parent Window's visual tree -->
<Button Command="{x:Static local:MainWindow.ShowMyDialogCommand}" />
<Image>
<Image.ContextMenu>
<ContextMenu>
<!-- Visual tree is different from the Window (due to the Popup).
Set CommandTarget to allow the command to traverse the visual tree
of the MainWindow to reach to the CommandBindng (defined by the MainWindow) -->
<MenuItem Header="Open Window"
Command="{x:Static local:MainWindow.ShowMyDialogCommand}"
CommandTarget="{Binding RelativeSource={RelativeSource
AncestorType=ContextMenu}, Path=PlacementTarget}" />
</ContextMenu>
</Image.ContextMenu>
</Image>
</StackPanel>
</Window>
Update
It turns out that the origin is really the 3rd party library. The implementation of the control is obviously really bad. It freezes the UI during construction/loading which is unacceptable.
Because the UI is frozen you can't even show a busy indicator. The user is left to believe that the application has crashed.
Such a library would make me doubt the authors skills and experience.
Because of the serious impact on the application's performance and UX I recommend to find an alternative library.
Even closing the ContextMenu forcefully does not solve the problem of a bad UX as the application still hangs.
The following solution extends the previous "Advanced MVVM compliant dialog flow" example. Following the "Advanced MVVM compliant dialog flow" will give you a clean design that helps to solve the issue more "gracefully" (I still recommend to find a better library).
The solution implements the following flow:
Instead of opening the dialog (which contains the terrible control) directly on clicking the MenuItem, we modify the flow to first close the ContextMenu.
This is accomplished by registering a ContextMenu.Opened event handler.
Next we spawn a second UI thread. Because any busy indicator that runs in the primary UI thread would freeze too, we use this dedicated new UI thread to show a busy indicator dialog. This way we can improve the UX significantly as from the user's point of view everything appears to be under control: just some heavy loading in the background.
In the main UI tread we create the instance of the dialog which is known to freeze the application (which will still freeze)
We use a SemaphoreSlim to allow the busy indicator dialog to wait asynchronously for a signal from the main UI thread in order to continue.
After the busy indicator thread received the signal, the busy indicator will close itself and shut down the second UI thread
The dialog cantaining the 3rd party control is now ready to use.
MainWindow.xaml
<Window>
<Image>
<Image.ContextMenu>
<ContextMenu Closed="OnImageContextMenuClosed">
<!-- Visual tree is different from the Window (due to the Popup).
Set CommandTarget to allow the command to traverse the visual tree
of the MainWindow to reach to the CommandBindng (defined by the MainWindow) -->
<MenuItem Header="Open Window"
Command="{x:Static local:MainWindow.ShowMyDialogCommand}"
CommandTarget="{Binding RelativeSource={RelativeSource
AncestorType=ContextMenu}, Path=PlacementTarget}" />
</ContextMenu>
</Image.ContextMenu>
</Image>
</Window>
MainWindow.xaml.cs
partial class MainWindow : Window
{
public static RoutedCommand ShowMyDialogCommand { get; } = new RoutedCommand("ShowMyDialogCommand", typeof(MainWindow));
private MainWindowViewModel MainWindowViewModel { get; }
public MainWindow()
{
InitializeComponent();
this.MainWindowViewModel = new MainWindowViewModel();
this.DataContext = this.MainWindowViewModel;
var showMyDialogCommandBinding = new CommandBinding(ShowMyDialogCommand, ExecuteShowMyDialogCommand, CanExecuteShowMyDialogCommand);
this.CommandBindings.Add(showMyDialogCommandBinding);
}
private void CanExecuteShowMyDialogCommand(object sender, CanExecuteRoutedEventArgs e)
=> e.CanExecute = true;
// Only close the ContextMenu. The ContextMenu.Closed event will continue the flow.
private void ExecuteShowMyDialogCommand(object sender, ExecutedRoutedEventArgs e)
=> (e.OriginalSource as FrameworkElement).ContextMenu.IsOpen = false;
private void OnImageContextMenuClosed(object? sender, EventArgs e)
{
// Create a semaphore that is initially blocking.
// The semaphore is used to signal the new UI thread that it must shut down
// and close the busy indicator dialog.
using var semaphore = new SemaphoreSlim(0, 1);
var uiThread = new Thread(state => ShowBusyIndicator(semaphore))
{
IsBackground = false
};
uiThread.SetApartmentState(ApartmentState.STA);
uiThread.Start();
(bool IsOk, OkDialogViewModel ViewModel) dialogResult = ShowOkDialog(semaphore);
// Do something when the user has closed the dialog e.g. using the 'OK' button
if (dialogResult.IsOk)
{
//dialogResult.ViewModel
}
}
private void ShowBusyIndicator(SemaphoreSlim semaphore)
{
// Consider to create a dedicated BusyIndicatorDialog class (following the pattern of the OkDialog).
// This allows to create a DataTemplate to design the dialog using XAML.
var busyIndicator = new Window()
{
Content = new ProgressBar() { IsIndeterminate = true },
Title = "Loading, please wait..."
};
// Let the busy indicator dialog wait for the SemaphoreSlim to signal.
// Consider to move this code directly to a dedicated BusyIndicatorDialog class.
// In case of implementing a dedicated BusyIndicatorDialog, consider to implement a special event which allows more control over the timing of the event (to replace the Loaded event).
busyIndicator.Loaded += (s, e) => OnBusyIndicatorLoaded(busyIndicator, semaphore);
busyIndicator.Show();
Dispatcher.Run();
}
// Use the Dispatcher of the busy indicator Window to post the code to the second UI thread.
private void OnBusyIndicatorLoaded(Window busyIndicator, SemaphoreSlim semaphore)
{
_ = busyIndicator.Dispatcher.InvokeAsync(async () =>
{
// Wait for the signal to continue the thread.
await semaphore.WaitAsync();
busyIndicator.Close();
busyIndicator.Dispatcher.BeginInvokeShutdown(DispatcherPriority.ContextIdle);
}, DispatcherPriority.ContextIdle);
}
private (bool IsOk, OkDialogViewModel ViewModel) ShowOkDialog(SemaphoreSlim semaphore)
{
var dialogViewModel = new OkDialogViewModel() { SomeText = "Just some text" };
var myDialog = new OkDialog()
{
Title = "I'm a Ok dialog",
DataContext = dialogViewModel
};
// Signal the busy indicator thread to continue (it will close itself and shut down the thread)
_ = semaphore.Release();
bool dialogResult = myDialog.ShowDialog() ?? false;
return (dialogResult, dialogViewModel);
}
}
I'l start by letting a picture do some talking.
So you see, I want to create a WPF user control that supports binding to a parent window's DataContext. The user control is simply a Button and a ListBox with a custom ItemTemplate to present things with a Label and a Remove Button.
The Add button should call an ICommand on the main view model to interact with the user in selecting a new thing (instance of IThing). The Remove buttons in the ListBoxItem in the user control should similarly call an ICommand on the main view model to request the related thing's removal. For that to work, the Remove button would have to send some identifying information to the view model about the thing requesting to be removed. So there are 2 types of Command that should be bindable to this control. Something like AddThingCommand() and RemoveThingCommand(IThing thing).
I got the functionality working using Click events, but that feels hacky, producing a bunch of code behind the XAML, and rubs against the rest of the pristine MVVM implementation. I really want to use Commands and MVVM normally.
There's enough code involved to get a basic demo working, I am holding off on posting the whole thing to reduce confusion. What is working that makes me feel like I'm so close is the DataTemplate for the ListBox binds the Label correctly, and when the parent window adds items to the collection, they show up.
<Label Content="{Binding Path=DisplayName}" />
While that displays the IThing correctly, the Remove button right next to it does nothing when I click it.
<Button Command="{Binding Path=RemoveItemCommand, RelativeSource={RelativeSource AncestorType={x:Type userControlCommands:ItemManager }}}">
This isn't terribly unexpected since the specific item isn't provided, but the Add button doesn't have to specify anything, and it also fails to call the command.
<Button Command="{Binding Path=AddItemCommand, RelativeSource={RelativeSource AncestorType={x:Type userControlCommands:ItemManager }}}">
So what I need is the "basic" fix for the Add button, so that it calls the parent window's command to add a thing, and the more complex fix for the Remove button, so that it also calls the parent command but also passes along its bound thing.
Many thanks for any insights,
This is trivial, and made so by treating your UserControl like what it is--a control (that just happens to be made up from other controls). What does that mean? It means you should place DependencyProperties on your UC to which your ViewModel can bind, like any other control. Buttons expose a Command property, TextBoxes expose a Text property, etc. You need to expose, on the surface of your UserControl, everything you need for it to do its job.
Let's take a trivial (thrown together in under two minutes) example. I'll leave out the ICommand implementation.
First, our Window
<Window x:Class="UCsAndICommands.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:t="clr-namespace:UCsAndICommands"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<t:ViewModel />
</Window.DataContext>
<t:ItemsEditor Items="{Binding Items}"
AddItem="{Binding AddItem}"
RemoveItem="{Binding RemoveItem}" />
</Window>
Notice we have our Items editor, which exposes properties for everything it needs--the list of items it is editing, a command to add a new item, and a command to remove an item.
Next, the UserControl
<UserControl x:Class="UCsAndICommands.ItemsEditor"
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:t="clr-namespace:UCsAndICommands"
x:Name="root">
<UserControl.Resources>
<DataTemplate DataType="{x:Type t:Item}">
<StackPanel Orientation="Horizontal">
<Button Command="{Binding RemoveItem, ElementName=root}"
CommandParameter="{Binding}">Remove</Button>
<TextBox Text="{Binding Name}" Width="100"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Button Command="{Binding AddItem, ElementName=root}">Add</Button>
<ItemsControl ItemsSource="{Binding Items, ElementName=root}" />
</StackPanel>
</UserControl>
We bind our controls to the DPs defined on the surface of the UC. Please, don't do any nonsense like DataContext=this; as this anti-pattern breaks more complex UC implementations.
Here's the definitions of these properties on the UC
public partial class ItemsEditor : UserControl
{
#region Items
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(
"Items",
typeof(IEnumerable<Item>),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public IEnumerable<Item> Items
{
get { return (IEnumerable<Item>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
#endregion
#region AddItem
public static readonly DependencyProperty AddItemProperty =
DependencyProperty.Register(
"AddItem",
typeof(ICommand),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public ICommand AddItem
{
get { return (ICommand)GetValue(AddItemProperty); }
set { SetValue(AddItemProperty, value); }
}
#endregion
#region RemoveItem
public static readonly DependencyProperty RemoveItemProperty =
DependencyProperty.Register(
"RemoveItem",
typeof(ICommand),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public ICommand RemoveItem
{
get { return (ICommand)GetValue(RemoveItemProperty); }
set { SetValue(RemoveItemProperty, value); }
}
#endregion
public ItemsEditor()
{
InitializeComponent();
}
}
Just DPs on the surface of the UC. No biggie. And our ViewModel is similarly simple
public class ViewModel
{
public ObservableCollection<Item> Items { get; private set; }
public ICommand AddItem { get; private set; }
public ICommand RemoveItem { get; private set; }
public ViewModel()
{
Items = new ObservableCollection<Item>();
AddItem = new DelegatedCommand<object>(
o => true, o => Items.Add(new Item()));
RemoveItem = new DelegatedCommand<Item>(
i => true, i => Items.Remove(i));
}
}
You are editing three different collections, so you may want to expose more ICommands to make it clear which you are adding/removing. Or you could cheap out and use the CommandParameter to figure it out.
Refer the below code.
UserControl.XAML
<Grid>
<ListBox ItemsSource="{Binding Things}" x:Name="lst">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ThingName}" Margin="3"/>
<Button Content="Remove" Margin="3" Command="{Binding ElementName=lst, Path=DataContext.RemoveCommand}" CommandParameter="{Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Window.Xaml
<Window x:Class="MultiBind_Learning.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MultiBind_Learning"
Title="Window1" Height="300" Width="300">
<StackPanel Orientation="Horizontal">
<Button Content="Add" Width="50" Height="25" Command="{Binding AddCommnd }"/>
<local:UserControl2/>
</StackPanel>
Window.xaml.cs
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.DataContext = new ThingViewModel();
}
}
ThingViewModel.cs
class ThingViewModel
{
private ObservableCollection<Thing> things = new ObservableCollection<Thing>();
public ObservableCollection<Thing> Things
{
get { return things; }
set { things = value; }
}
public ICommand AddCommnd { get; set; }
public ICommand RemoveCommand { get; set; }
public ThingViewModel()
{
for (int i = 0; i < 10; i++)
{
things.Add(new Thing() { ThingName="Thing" +i});
}
AddCommnd = new BaseCommand(Add);
RemoveCommand = new BaseCommand(Remove);
}
void Add(object obj)
{
things.Add(new Thing() {ThingName="Added New" });
}
void Remove(object obj)
{
things.Remove((Thing)obj);
}
}
Thing.cs
class Thing :INotifyPropertyChanged
{
private string thingName;
public string ThingName
{
get { return thingName; }
set { thingName = value; OnPropertyChanged("ThingName"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
BaseCommand.cs
public class BaseCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _method;
public event EventHandler CanExecuteChanged;
public BaseCommand(Action<object> method)
{
_method = method;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_method.Invoke(parameter);
}
}
Instead of Base command you can try RelayCommand from MVVMLight or DelegateCommand from PRISM libraries.
By default, your user control will inherit the DataContext of its container. So the ViewModel class that your window uses can be bound to directly by the user control, using the Binding notation in XAML. There's no need to specify DependentProperties or RoutedEvents, just bind to the command properties as normal.
We have a WPF application written using the MVVM pattern. Within the application is a TabControl with different UserControls within each tab. Under certain conditions one of the UserControls on a tab can take a significant portion of time to load when switching to the containing tab.
This is NOT because of any performance bottlenecks in the ViewModel. But instead, is due to significant amount of time that the usercontrol takes to bind to the ViewModel, and to create the various UI elements contained within it and initialize them.
When the user clicks on the tab for this usercontrol, the UI becomes completely unresponsive until the control has completed loading. If fact you don't even see the "active tab" switch until everything is loaded.
What strategies could I use to display a "spinner" with some sort of "please wait, loading..." message while waiting for the UI elements to complete loading?
UPDATE with sample code:
The below demonstrates the type of problem I am trying to get around. When you click on the "slow tab". The UI becomes unresponsive until all the items in the slow tab have rendered.
In the below, TestVM is the viewmodel for the slow tab. It has a large collection of children objects. Each created with it's own data template.
How could I display a "loading" message while the slow tab finishes loading?
public class MainVM
{
private TestVM _testVM = new TestVM();
public TestVM TestVM
{
get { return _testVM; }
}
}
/// <summary>
/// TestVM is the ViewModel for the 'slow tab'. It contains a large collection of children objects that each will use a datatemplate to render.
/// </summary>
public class TestVM
{
private IEnumerable<ChildBase> _children;
public TestVM()
{
List<ChildBase> list = new List<ChildBase>();
for (int i = 0; i < 100; i++)
{
if (i % 3 == 0)
{
list.Add(new Child1());
}
else if (i % 3 == 1)
{
list.Add(new Child2());
}
else
{
list.Add(new Child3());
}
}
_children = list;
}
public IEnumerable<ChildBase> Children
{
get { return _children; }
}
}
/// <summary>
/// Just a base class for a randomly positioned VM
/// </summary>
public abstract class ChildBase
{
private static Random _rand = new Random(1);
private int _top = _rand.Next(800);
private int _left = _rand.Next(800);
public int Top { get { return _top; } }
public int Left { get { return _left; } }
}
public class Child1 : ChildBase { }
public class Child2 : ChildBase { }
public class Child3 : ChildBase { }
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<!-- Template for the slow loading tab -->
<DataTemplate DataType="{x:Type local:TestVM}">
<ItemsControl ItemsSource="{Binding Children}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True"></Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="FrameworkElement">
<Setter Property="Canvas.Top" Value="{Binding Top}"></Setter>
<Setter Property="Canvas.Left" Value="{Binding Left}"></Setter>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</DataTemplate>
<!-- examples of different child templates contained in the slow rendering tab -->
<DataTemplate DataType="{x:Type local:Child1}">
<DataGrid></DataGrid><!--simply an example of a complex control-->
</DataTemplate>
<DataTemplate DataType="{x:Type local:Child2}">
<RichTextBox Width="30" Height="30">
<!--simply an example of a complex control-->
</RichTextBox>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Child3}">
<Calendar Height="10" Width="15"></Calendar>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl>
<TabItem Header="Fast Loading tab">
<TextBlock Text="Not Much Here"></TextBlock>
</TabItem>
<TabItem Header="Slow Tab">
<ContentControl Content="{Binding TestVM}"></ContentControl>
</TabItem>
</TabControl>
</Grid>
</Window>
What do u need is here
http://msdn.microsoft.com/en-us/library/ms741870.aspx
public partial class Window1 : Window
{
// Delegates to be used in placking jobs onto the Dispatcher.
private delegate void NoArgDelegate();
private delegate void OneArgDelegate(String arg);
// Storyboards for the animations.
private Storyboard showClockFaceStoryboard;
private Storyboard hideClockFaceStoryboard;
private Storyboard showWeatherImageStoryboard;
private Storyboard hideWeatherImageStoryboard;
public Window1(): base()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Load the storyboard resources.
showClockFaceStoryboard =
(Storyboard)this.Resources["ShowClockFaceStoryboard"];
hideClockFaceStoryboard =
(Storyboard)this.Resources["HideClockFaceStoryboard"];
showWeatherImageStoryboard =
(Storyboard)this.Resources["ShowWeatherImageStoryboard"];
hideWeatherImageStoryboard =
(Storyboard)this.Resources["HideWeatherImageStoryboard"];
}
private void ForecastButtonHandler(object sender, RoutedEventArgs e)
{
// Change the status image and start the rotation animation.
fetchButton.IsEnabled = false;
fetchButton.Content = "Contacting Server";
weatherText.Text = "";
hideWeatherImageStoryboard.Begin(this);
// Start fetching the weather forecast asynchronously.
NoArgDelegate fetcher = new NoArgDelegate(
this.FetchWeatherFromServer);
fetcher.BeginInvoke(null, null);
}
private void FetchWeatherFromServer()
{
// Simulate the delay from network access.
Thread.Sleep(4000);
// Tried and true method for weather forecasting - random numbers.
Random rand = new Random();
String weather;
if (rand.Next(2) == 0)
{
weather = "rainy";
}
else
{
weather = "sunny";
}
// Schedule the update function in the UI thread.
tomorrowsWeather.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal,
new OneArgDelegate(UpdateUserInterface),
weather);
}
private void UpdateUserInterface(String weather)
{
//Set the weather image
if (weather == "sunny")
{
weatherIndicatorImage.Source = (ImageSource)this.Resources[
"SunnyImageSource"];
}
else if (weather == "rainy")
{
weatherIndicatorImage.Source = (ImageSource)this.Resources[
"RainingImageSource"];
}
//Stop clock animation
showClockFaceStoryboard.Stop(this);
hideClockFaceStoryboard.Begin(this);
//Update UI text
fetchButton.IsEnabled = true;
fetchButton.Content = "Fetch Forecast";
weatherText.Text = weather;
}
private void HideClockFaceStoryboard_Completed(object sender,
EventArgs args)
{
showWeatherImageStoryboard.Begin(this);
}
private void HideWeatherImageStoryboard_Completed(object sender,
EventArgs args)
{
showClockFaceStoryboard.Begin(this, true);
}
}
P.S. Perhaps it is useful as well http://tech.pro/tutorial/662/csharp-tutorial-anonymous-delegates-and-scoping and Make dispatcher example to work
Make your control lazy-load its contents.
To do that expose an ObservableCollection property in your TestVM class and attach event handlers to CollectionChanged (possible PropertyChanged too) to add actual UI elements.
In Window1 prepare the data to load in TestVM on a separate thread (are you doing any web queries?), pass the data to TestVM on the UI thread.
If TestVM child controls themselves load slowly you can split that drive that process from a separate thread too but that's (way) more difficult to pull of, so hopefully it's the data loading that's the slow part
Causes could be Slow Code in a Binding Converter, Coerce Value Callback, Properties could all make a binding appear slow. For Example, Consider An Image whose source binds to a URL. This could load slow due to network lags.
Also avoid switching to dispatcher context - unless really needed. For example to launch threads, Waiting for WaitHandles, even large/slow synchronous I/O operations etc etc
Sten Petrov's suggestion to lazy load (UI and Data Virtualization) is vital too.
I have a WPF app with MVVM pattern and it contains following 2 views:
1:MainWindow.xaml (it's a window)
below is main portion of MainWindow.xaml:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:XliffListViewModel}">
<vw:XliffListView />
</DataTemplate>
</Window.Resources>
<Grid Margin="4">
<Border Background="GhostWhite" BorderBrush="LightGray" BorderThickness="1" CornerRadius="5" >
<ItemsControl ItemsSource="{Binding ViewModels}" Margin="4" />
</Border>
</Grid>
2:XliffListView.xaml (it's a user control)
XliffListView contain a datagrid and a button for save all changes that happens
I want to show messagebox when user closing app if changes not saved
You can write and Attached behavior that handles Window.Closing() event of the Window and executes the ClosingCommand from ViewModel and returns true or false as parameter so that one can cancel (e.Cancel) closing event if the VM wants to stop th window from closing.
The code below is just for concept and is not complete
XAML
<Window x:Class="..."
...
myBheaviors:WindowBehaviors.ClosingCommand="{Binding MyClosingCommand}">
...
</Window>
ViewModel
public class MyWindowViewModel
{
public ICommand MyClosingCommand
{
get
{
if (_myClosingCommand == null)
_myClosingCommand
= new DelegateCommand<CancelEventArgs>(OnClosing);
return _myClosingCommand;
}
}
private void OnClosing(CancelEventArgs e)
{
if (this.Dirty) //Some function that decides if the VM has pending changes.
e.Cancel = true;
}
}
Attached Behavior
public static class WindowBehaviors
{
public static readonly DependencyProperty ClosingCommandProperty
= DependencyProperty.RegistsrrAttached(
"ClosingCommand",
...,
new PropertyMetataData(OnClosingCommandChanged);
public static ICommand GetClosingCommand(...) { .. };
public static void SetClosingCommand(...) { .. };
private static void OnClosingCommandChanged(sender, e)
{
var window = sender as Window;
var command = e.NewValue as ICommand;
if (window != null && command != null)
{
window.Closing
+= (o, args) =>
{
command.Execute(args);
if (args.Cancel)
{
MessageBox.Show(
"Window has pending changes. Cannot close");
// Now window will be stopped from closing.
}
};
}
}
}
EDIT:
For user controls, instead of Closing use Unloaded event.
Also try to establish a hierarchy between ViewModels i.e.e Window's ViewModel should contain UserControl's ViewModel. So that Closing event's IsDirty() call can scrutinize the UserControls' ViewModel also.
Here is the working code i have: The text and background color property do change when I click the button (but for a micro second) and are then set back to the default text/color. Seems like RaisePropertyChanged is being triggered again and again. Can somebody help point what I am doing wrong?
MainWindow.xaml code
<Window x:Class="BuiltIn_Custom_Commands_Eg.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel HorizontalAlignment="Center">
<TextBlock Text="{Binding txtblck_text, StringFormat=Default: {0}}" Padding="10" FontStyle="Italic" Background="{Binding txtblck_color}"/>
<Button Content="Change Color" Width="100" Height="30" Margin="20" Command="{Binding OkCommand}" />
</StackPanel>
</Grid>
ViewModel Code:
class Example_ViewModel : ViewModelBase
{
#region Properties
private string _txtblck_text;
private Brush _txtblck_color;
public ICommand OkCommand {get; set;}
public string txtblck_text
{
get { return _txtblck_text; }
set
{
_txtblck_text = value;
RaisePropertyChanged("txtblck_text");
}
}
public Brush txtblck_color
{
get { return _txtblck_color; }
set
{
_txtblck_color = value;
RaisePropertyChanged("txtblck_color");
}
}
#endregion
#region Constructor
public Example_ViewModel()
{
OkCommand = new myCommand(myOkExecute, myCanOkExecute);
}
#endregion
private void myOkExecute(object parameter)
{
txtblck_color = Brushes.CadetBlue;
//RaisePropertyChanged("txtblck_color");
txtblck_text = "You Clicked me!!!";
//RaisePropertyChanged("txtblck_text");
}
private bool myCanOkExecute(object parameter)
{
txtblck_color = Brushes.Yellow;
txtblck_text = "You havent clicked me!!!";
return true;
}
}
The CanExecute method will and should be called whenever bindings change. Therefore changing a binding in the Execute method (color) will cause CanExecute to be called again.
Instead, why dont you initialize the colors private member once in the constructor as follows.
public Example_ViewModel()
{
OkCommand = new myCommand(myOkExecute, myCanOkExecute);
_txtblck_color = = Brushes.Yellow;
}
Note, the same is also true for the text property. Normally all property private member should be set up with defaults on initialize (constructor) as this avoids unnecessary calls to INotifyPropertyChanged.
Also, in order to test how the code is behaving and to confirm this just set some breakpoints in the CanExecute method to see how the program flow is behaving.
Your problem is that you shouldn't do any setting of your properties in your myCanOkExecute...because it is that that is being called and changing your properties back to the yellow, etc.
The CanExecute methods of Commands could be called multiple times and sometimes when you don't expect ...e.g. when the focus changes to a different control, when certain controls are being edited/sent keypress, after a Command has been executed, when someone calls CommandManager.InvalidateRequerySuggested, etc.
Thus what's happening is your myCanOkExecute is being called shortly after you have clicked and executed your button.