Simple explanation of how to clean up unmanaged resources in WPF - c#

I have an app using MVVM Light which launches another app that I need to stop when the user closes the main app down.
I have created a Dispose() method that frees up resources, but what I don't understand is where I call Dispose().
For example, I noticed in the Application class definition there are is: public event ExitEventHandler Exit;
Can I add something to my App (see code below) that is triggered when the app is about to exit?
(I know there are many other question on this topic but they all seem to presume more c# knowledge)
App.xaml.cs...
namespace Module.Config
{
public partial class App : Application
{
static App()
{
DispatcherHelper.Initialize();
}
}
}
MainWindow.xaml...
<Window x:Class="Module.Config.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:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
Height="640" MinHeight="600"
Width="800" MinWidth="800"
Title="Config"
DataContext="{Binding Main, Source={StaticResource Locator}}"
Closing="Window_Closing"
>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot" Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ContentControl Content="{Binding Side}" Grid.Column="0" />
<ContentControl Content="{Binding SelectedModule}" Grid.Column="1" />
</Grid>
</Window>
MainWindow.xaml.cs...
namespace Module.Config
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Closing += (s, e) => ViewModelLocator.Cleanup();
Messenger.Default.Register<DialogMessage>(
this,
msg =>
{
var result = MessageBox.Show(
msg.Content,
msg.Caption,
msg.Button
);
msg.ProcessCallback(result);
});
. . . . .
private void Window_Closing(object sender, CancelEventArgs e)
{
/*
I want to call Dispose() within AvigilonViewModel.cs
*/
}
}
}
AvigilonView.xaml...
<UserControl x:Class="Module.Config.Views.Modules.AvigilonView"
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:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:Module.Config"
xmlns:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
DataContext="{Binding Avigilon, Source={StaticResource Locator}}">
<Grid>
.....
AvigilonView.xaml.cs...
namespace Module.Config.Views.Modules
{
public partial class AvigilonView : UserControl
{
public AvigilonView()
{
InitializeComponent();
}
}
}
MainViewModel.cs......
namespace Module.Config.ViewModel
{
public class MainViewModel : ViewModelBase
{
private ViewModelBase m_selectedModule;
public ViewModelBase Side
{
get { return ServiceLocator.Current.GetInstance<SideViewModel>(); }
}
public ViewModelBase SelectedModule
{
get { return m_selectedModule; }
set
{
m_selectedModule = value;
RaisePropertyChanged("SelectedModule");
}
}
public MainViewModel()
{
Messenger.Default.Register<PropertyChangedMessage<object>>(this, (r) =>
{
if (r.PropertyName == "SelectedNode")
{
if (r.NewValue is RedSensor)
{
SelectedModule = ServiceLocator.Current.GetInstance<SensorViewModel>();
(SelectedModule as SensorViewModel).Sensor = r.NewValue as RedSensor;
}
else
{
SelectedModule = r.NewValue as ViewModelBase;
}
}
});
}
}
}
AvigilonViewModel.cs....
namespace Module.Config.ViewModel.Modules
{
public class AvigilonViewModel : ViewModelBase
{
....
public Dispose(){
/* code to get rid of unmanaged resources */
}
}
}
ViewModelLocator.cs...
namespace Module.Config.ViewModel
{
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
//SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
//SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<AppState>();
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<SideViewModel>();
SimpleIoc.Default.Register<AvigilonViewModel>();
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public SideViewModel Side
{
get
{
return ServiceLocator.Current.GetInstance<SideViewModel>();
}
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public AvigilonViewModel Avigilon
{
get
{
return ServiceLocator.Current.GetInstance<AvigilonViewModel>();
}
}
....
I've also noticed the line Closing += (s, e) => ViewModelLocator.Cleanup(); in the above. I have tried adding a method called `Cleanup() to AvigilonView.xaml.cs and AvigilonViewModel.cs but it doesnt seem to be getting called.

You can handle the Closing event of your MainWindow and do the processing there.
XAML:
<Window Closing="Window_Closing">
Code behind:
private void Window_Closing(object sender, CancelEventArgs e)
{
// Clean up your resources here.
}

Related

How to open multiple windows in a queue, each window waiting for previous to close?

I'm trying to create a primitive notifications system.
This is how I show my window:
var data = new NotificationData {
Title = "New order",
OrderId = "1"
}
var notification = new NotificationWindow(data);
Activated += (s, e) => { notification.Owner = this; };
notification.Show();
Then the NotificationWindow just displays provided data. But if there are multiple notifications, they all stack in one spot, I need them to appear after previous is closed.
I tried to use ShowDialog() instead and it did work - but the MainWindow hangs when notification is open. Running it in a different thread using Task.Run() causes notifications not to appear.
You need to register to the closed event and then check if another notification is queued.
The key is the ShowOrQueueNotification which checks if there is a notification present or not. If there is one present, queue the new one and when the notification is closed, you need to check if there is one queued.
I've made an example to show how to use a queue for it:
MainWindow.xaml
<Window x:Class="TestNotificationQueue.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:TestNotificationQueue"
mc:Ignorable="d"
Title="MainWindow" Height="158.868" Width="266.385">
<Grid>
<Border Background="Yellow" VerticalAlignment="Center" HorizontalAlignment="Center" BorderThickness="1" BorderBrush="Black" >
<StackPanel Margin="20">
<TextBox x:Name="TextBoxMessage" Width="192" />
<Button Content="ShowNotification" Width="128" Height="24" Click="Button_Click" Margin="8"/>
</StackPanel>
</Border>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Windows;
namespace TestNotificationQueue
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private Queue<NotificationInfo> _notificationQueue = new Queue<NotificationInfo>();
private NotificationWindow _currentNotificationWindow;
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ShowOrQueueNotification(new NotificationInfo(TextBoxMessage.Text));
}
private void ShowOrQueueNotification(NotificationInfo notificationInfo)
{
// -----> If no notification is presented, create one.
if (_currentNotificationWindow == null)
{
_currentNotificationWindow = new NotificationWindow(notificationInfo);
_currentNotificationWindow.Closed += CurrentNotificationWindow_Closed;
_currentNotificationWindow.Show();
}
else
// -----> queue it.
_notificationQueue.Enqueue(notificationInfo);
}
private void CurrentNotificationWindow_Closed(object sender, EventArgs e)
{
// -----> This is crucial, you need to set the current to null, else all new notification will be queued and never be presented.
_currentNotificationWindow = null;
if(_notificationQueue.Count > 0)
ShowOrQueueNotification(_notificationQueue.Dequeue());
}
}
}
Notification.cs
namespace TestNotificationQueue
{
public class NotificationInfo
{
public NotificationInfo(string message)
{
Message = message;
}
public string Message { get; }
}
}
NotificationWindow.xaml
<Window x:Class="TestNotificationQueue.NotificationWindow"
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:TestNotificationQueue"
mc:Ignorable="d"
Title="NotificationWindow" Height="93.117" Width="239.4">
<Grid>
<TextBlock x:Name="TextBoxMessage" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="30" />
</Grid>
</Window>
NotificationWindow.xaml.cs
using System.Windows;
namespace TestNotificationQueue
{
/// <summary>
/// Interaction logic for NotificationWindow.xaml
/// </summary>
public partial class NotificationWindow : Window
{
public NotificationWindow(NotificationInfo notificationInfo)
{
InitializeComponent();
TextBoxMessage.Text = notificationInfo.Message;
}
}
}

INotifyPropertyChanged with 2 Windows

I have a simple WPF application with 2 windows.
I am trying to update a textblock and a checkbox on MainWindow when a user input something on SubWindow.
There are two variables that I set to pass values between windows.
Properties.Settings.Default.strText
Properties.Settings.Default.isChecked
MainWindow.xaml
<Window x:Class="PropertyChangedExample.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:PropertyChangedExample"
mc:Ignorable="d"
Title="MainWindow" Height="400" Width="400"
Focusable="False">
<Grid>
<TextBlock x:Name="TxtBlock" Text="{Binding TxtBinding}" Width="200" Height="30" Margin="0,100,0,0"/>
<CheckBox IsChecked="{Binding IsChecked}" Width="25" Height="25" />
</Grid>
</Window>
MainWindow.xaml.cs
using System.ComponentModel;
using System.Windows;
namespace PropertyChangedExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
SubWindow subWindow = new SubWindow();
subWindow.Show();
DataContext = new DataContextExample();
}
}
public class DataContextExample : INotifyPropertyChanged
{
public string TxtBinding
{
get { return Properties.Settings.Default.strText; }
set
{
Properties.Settings.Default.strText = value;
OnPropertyChanged(nameof(TxtBinding));
}
}
public bool IsChecked
{
get { return Properties.Settings.Default.isChecked; }
set
{
Properties.Settings.Default.isChecked = value;
OnPropertyChanged(nameof(IsChecked));
}
}
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
SubWindow.xaml
<Window x:Class="PropertyChangedExample.SubWindow"
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:PropertyChangedExample"
mc:Ignorable="d"
Title="SubWindow" Height="100" Width="250">
<Grid>
<TextBox KeyUp="Window_KeyUp" x:Name="TxtBox" Width="200" Height="30" />
</Grid>
</Window>
SubWindow.xaml.cs
using System.Windows;
using System.Windows.Input;
namespace PropertyChangedExample
{
/// <summary>
/// Interaction logic for SubWindow.xaml
/// </summary>
public partial class SubWindow : Window
{
public SubWindow()
{
InitializeComponent();
}
private void Window_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
Properties.Settings.Default.strText = TxtBox.Text;
}
else if (e.Key == Key.LeftCtrl)
{
Properties.Settings.Default.isChecked = true;
}
else if (e.Key == Key.LeftShift)
{
Properties.Settings.Default.isChecked = false;
}
}
}
}
When a user enter anything on the textbox on SubWindow, the textblock on MainWindow should be updated. Also, the checkbox should be either checked and unchecked if a user press either left ctrl or shift key. So far, nothing happens. What am I missing here?
You need to pass the DataContext to your second window, or else it has no way of knowing about the data used in your MainWindow. This can be done in the second window's constructor.
DataContextExample myContext;
public SubWindow(DataContextExample context)
{
myContext = context;
DataContext = myContext;
InitializeComponent();
}
Then call it like so in from your MainWindow
public MainWindow()
{
InitializeComponent();
DataContextExample context = new DataContextExample();
SubWindow subWindow = new SubWindow(context);
subWindow.Show();
DataContext = context;
}
Also, in your second window you are changing Properties.Setting.Default values directly, but you need to change the DataContext value.
private void Window_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
myContext.strText = TxtBox.Text;
}
//....
}
The best solution i can see for this kind of situation is messagin or know as Publisher/Subscriber pattern, in your sub window you create an event and publish it to whatever lessner (subscriber). here is a simple example :
https://www.c-sharpcorner.com/UploadFile/pranayamr/publisher-or-subscriber-pattern-with-event-or-delegate-and-e/

MVVM Hierarchical Navigation tutorial StackOverFlow exception

I'm trying to reproduce a MVVM tutorial for WPF but applying it to UWP. But I've done everything in the tutorial I believe right the exact same code shown at the tutorial.
But when I ran the code I kept getting a StackOverflowException which is caused because the MainPageView keeps initializing again and again, until the exception is thrown.
The thing is I'm kinda knew at MVVM and I wish to master it, so can somebody please explain me why am I getting this?
I'll leave the code of each one of my classes and views.
This is my MainPageView.Xaml:
<Page
x:Class="MVVMHierarchiesDemo.MainPageView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MVVMHierarchiesDemo"
xmlns:views="using:MVVMHierarchiesDemo.Views"
xmlns:viewmodel="using:MVVMHierarchiesDemo.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<!--Anytime the current view model is set to an instance of a CustomerListViewModel,
it will render out a CustomerListView with the ViewModel is hooked up. It’s an order ViewModel,
it'll render out OrderView and so on.
We now need a ViewModel that has a CurrentViewModel property and some logic and commanding
to be able to switch the current reference of ViewModel inside the property.-->
<Page.DataContext>
<local:MainPageView/>
</Page.DataContext>
<Page.Resources>
<DataTemplate x:Key="CustomerTemplate" x:DataType="viewmodel:CustomerListViewModel">
<views:CustomerListView/>
</DataTemplate>
<DataTemplate x:Key="OrderTemplate" x:DataType="viewmodel:OrderViewModel">
<views:OrderView/>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid x:Name="NavBar"
Grid.Row="0">
<Button Content="Customers"
Command="{Binding NavCommand}"
CommandParameter="customers"
Grid.Column="0"
Grid.Row="0"/>
<Button Content="Orders"
Command="{Binding NavCommand}"
CommandParameter="orders"
Grid.Column="2"
Grid.Row="0"/>
</Grid>
<Grid x:Name="MainContent"
Grid.Row="1">
<ContentControl Content="{Binding CurrentViewModel}"/>
</Grid>
</Grid>
</Page>
This is my code-behind MainPageView.xaml.cs - here is where the StackoverflowException is thrown in the constructor it keeps calling it.
using Windows.UI.Xaml.Controls;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace MVVMHierarchiesDemo
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPageView : Page
{
public MainPageView()
{
this.InitializeComponent();
}
}
}
This is my BindableBase.cs as the tutorial shows:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MVVMHierarchiesDemo
{
/*The main idea behind this class is to encapsulate the INotifyPropertyChanged implementation
* and provide helper methods to the derived class so that they can easily trigger the appropriate notifications.
* Following is the implementation of BindableBase class.*/
public class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void SetProperty<T>(ref T member, T val, [CallerMemberName]string propertyName = null)
{
if (object.Equals(member, val))
return;
member = val;
OnPropertyChanged(propertyName);
}
}
}
This is MyCommand.cs or better known as the relay command pattern:
using System;
using System.Windows.Input;
namespace MVVMHierarchiesDemo
{
/* Now it's time to actually start doing some view switching using our CurrentViewModel property.
* We just need some way to drive the setting of this property. And we're going to make it so that
* the end user can command going to the customer list or to the order view. First add a new class
* in your project which will implement the ICommand interface. Following is the implementation of
* ICommand interface.*/
public class MyCommand<T> : ICommand
{
Action<T> _TargetExecuteMethod;
Func<T, bool> _TargetCanExecuteMethod;
public MyCommand(Action<T> targetExecuteMethod)
{
_TargetExecuteMethod = targetExecuteMethod;
}
public MyCommand(Action<T> targetExecuteMethod, Func<T,bool> targetCanExecuteMethod)
{
_TargetExecuteMethod = targetExecuteMethod;
_TargetCanExecuteMethod = targetCanExecuteMethod;
}
public event EventHandler CanExecuteChanged = delegate { };
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
bool ICommand.CanExecute(object parameter)
{
if (_TargetCanExecuteMethod != null)
{
T tparam = (T)parameter;
return _TargetCanExecuteMethod(tparam);
}
if (_TargetExecuteMethod != null)
return true;
return false;
}
void ICommand.Execute(object parameter)
{
if(_TargetExecuteMethod!=null)
{
T tparam = (T)parameter;
_TargetExecuteMethod(tparam);
}
}
}
}
This is my usercontrol for OrdersView.xaml:
<UserControl
x:Class="MVVMHierarchiesDemo.Views.OrderView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MVVMHierarchiesDemo.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<TextBlock Text="Order View"/>
</Grid>
</UserControl>
This is my user control CustomerListView.xaml:
<UserControl
x:Class="MVVMHierarchiesDemo.Views.CustomerListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MVVMHierarchiesDemo.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<TextBlock Text="Customer List View"/>
</Grid>
</UserControl>
This is my OrderViewModel:
namespace MVVMHierarchiesDemo.ViewModel
{
/*Derive all of your ViewModels from BindableBase class.*/
public class OrderViewModel : BindableBase
{
}
}
This is my CustomerViewModel:
namespace MVVMHierarchiesDemo.ViewModel
{
/*Derive all of your ViewModels from BindableBase class.*/
public class CustomerListViewModel : BindableBase
{
}
}
Finally this is my MainPageViewModel:
namespace MVVMHierarchiesDemo.ViewModel
{
/*Derive all of your ViewModels from BindableBase class.*/
public class MainPageViewModel : BindableBase
{
public MainPageViewModel()
{
NavCommand = new MyCommand<string>(OnNavigation);
}
private CustomerListViewModel _customerListViewModel = new CustomerListViewModel();
private OrderViewModel _orderViewModel = new OrderViewModel();
private BindableBase _currentViewModel;
public BindableBase CurrentViewModel
{
get
{
return _currentViewModel;
}
set
{
SetProperty(ref _currentViewModel, value);
}
}
public MyCommand<string> NavCommand { get; private set; }
private void OnNavigation(string destination)
{
switch (destination)
{
case "orders":
{
CurrentViewModel = _orderViewModel;
break;
}
case "customers":
default:
CurrentViewModel = _customerListViewModel;
break;
}
}
}
}
and lastly I think the MainPageView is the one causing the infinite looping but I don't understand why?
If somebody could be so kind to tell me what I am doing wrong on UWP?
Also I could use MVVM Light or MVVMCross I'm not interested on those solutions I want to learn MVVM by hand and later on i might check those frameworks.
It's because in your MainPageView.xaml you have this:
<Page.DataContext>
<local:MainPageView/>
</Page.DataContext>
So every MainPageview creates a nested MainPageView as its DataContext. These are created until you blow the stack.
I think you meant to put a MainPageViewModel in here.

XAML - recursive game logic

I am working on small game now and the logic works with Attack method from ModelView class which it should be called every 1 second and also needs to beggin OnStartup . How should i implement it?
public class MainModelView: BaseViewModel
{
private ICommand attack;
public MainModelView()
{
}
public ICommand Attack
{
get
{
if (this.attack == null)
{
this.attack = new RelayCommand(this.PerformAttack);
}
return this.attack;
}
}
private void PerformAttack(object obj);
}
And XAML
<Window x:Class="ConsoleHeroes.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lib="clr-namespace:ConsoleHeroes.ViewModels"
Title="ConsoleHeroes" Width="1200" Height="700" Name="Main">
<Window.DataContext>
<lib:MainModelView/>
</Window.DataContext>
<Grid>
</Grid>
</Window>

Binding is not working on custom user control's dependency property

I've been working at this for awhile and seeming to not be able to find any good answers to my problem. I'm using a custom control that has custom dependency properties and in my main app I am binding to those propertys with my viewmodel that is seen through a viewmodel locator using mvvmlight. my question is why is the binding not updating nor seeing the correct datacontext?
Code:
User Control Xaml:
<UserControl x:Name="zKeyBoard"
x:Class="ZLibrary.ZKeyBoard"
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"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="768" d:DesignWidth="1024">
<Grid>
<TextBox TextWrapping="Wrap" Text="{Binding zDisplayText}" />
<Label Content="{Binding zBoxToEdit}"/>
</Grid>
</UserControl>
Things I have Tried In The User Control Xaml Already:
<TextBox TextWrapping="Wrap" Text="{Binding zDisplayText, ElementName=zKeyBoard}" />
<Label Content="{Binding zBoxToEdit, ElementName=zKeyBoard}"/>
User Control C#:
using System.ComponentModel;
namespace ZLibrary
{
public partial class ZKeyBoard : UserControl, INotifyPropertyChanged
{
public ZKeyBoard()
{
InitializeComponent();
}
public string zBoxToEdit
{
get { return (string)GetValue(zBoxToEditProperty); }
set { SetValue(zBoxToEditProperty, value); }
}
public static readonly DependencyProperty zBoxToEditProperty =
DependencyProperty.Register("zBoxToEdit", typeof(string), typeof(ZKeyBoard), new UIPropertyMetadata(""));
public string zDisplayText
{
get { return (string)GetValue(zDisplayTextProperty); }
set { SetValue(zDisplayTextProperty, value); }
}
public static readonly DependencyProperty zDisplayTextProperty =
DependencyProperty.Register("zDisplayText", typeof(string), typeof(ZKeyBoard), new UIPropertyMetadata(""));
}
}
Things I have already tried in the user control C#:
public string zBoxToEdit
{
get;
set;
}
public string zDisplayText
{
get;
set;
}
Here is the Project Files Where the User Control Is Being Used:
APP.xaml:
<Application x:Class="WpfApplication1.App"
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:vm="clr-namespace:Sandstorm.ViewModel"
mc:Ignorable="d"
StartupUri="Main.xaml">
<Application.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
</ResourceDictionary>
</Application.Resources>
</Application>
The ViewModel Locator:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
namespace Sandstorm.ViewModel
{
class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<KeyBoardViewModel>(() =>
{
return new KeyBoardViewModel();
});
}
public KeyBoardViewModel KeyBoardViewModel
{
get { return ServiceLocator.Current.GetInstance<KeyBoardViewModel>(); }
}
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
}
The Xaml The User Control Is Being Used In:
<Page x:Name="keyboard_Frame"
x:Class="Sandstorm.keyBoard"
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:ZControls="clr-namespace:ZLibrary;assembly=ZLibrary"
DataContext="{Binding KeyBoardViewModel, Source={StaticResource Locator}}"
mc:Ignorable="d"
d:DesignHeight="768" d:DesignWidth="1024"
ShowsNavigationUI="False"
Title="KeyBoard">
<Grid>
<ZControls:ZKeyBoard zBoxToEdit="{Binding boxToEdit}" zDisplayText="{Binding keyboardEntry}" />
</Grid>
</Page>
When This Xaml is ran as is This Way It Throws an error in the console that says it can not find the binding property of either boxToEdit or keyboarEntry and it refrences the original ZKeyBoard Name as The place it can not be found... So I added this:
<ZControls:ZKeyBoard zBoxToEdit="{Binding boxToEdit, RelativeSource={RelativeSource Mode=TemplatedParent}}" zDisplayText="{Binding keyboardEntry, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
Which caused the error to go away which I assume meant that it could find the viewmodel yet still nothing happened.
And Finally The View Model:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.ComponentModel;
namespace Sandstorm.ViewModel
{
class KeyBoardViewModel : ViewModelBase, INotifyPropertyChanged
{
private string _keyboardEntry;
private string _boxToEdit;
public KeyBoardViewModel()
{
_boxToEdit = "yay";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
public string keyboardEntry
{
get { return this._keyboardEntry; }
set
{
if (this._keyboardEntry != value)
{
this._keyboardEntry = value;
this.OnPropertyChanged("keyboardEntry");
Console.Out.WriteLine(this._keyboardEntry);
}
}
}
public string boxToEdit
{
get { return this._boxToEdit; }
set
{
if (this._boxToEdit != value)
{
this._boxToEdit = value;
this.OnPropertyChanged("boxToEdit");
Console.Out.WriteLine(this._boxToEdit);
}
}
}
}
}
One Thing I noticed was that I can Not See The Console.out.writeline doing anything which to me means it is not setting at all. so lots of big questions as to why this is not working. Any Help on this would be amazing! it probably is something small and stupid but a second pair of eyes on this will probably notice it faster than me.
Simple answer:
Don't set the DataContext to self.
Problem resolved

Categories