I have problems understanding the binding to a property in a subclass.
This is my code:
MainWindow.XAML
<Window x:Class="Datagrid_vs_Database.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:Datagrid_vs_Database"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<TextBlock x:Name="txtPath" Text="{Binding Path=CurrentDatabase.FullFilePath, UpdateSourceTrigger=PropertyChanged}"/>
<Button Command="{Binding openFileBrowser}" Content="New Database"/>
</StackPanel>
</Grid>
Manager.cs
public class Manager
{
private static readonly Manager instance = new Manager();
public static Manager Instance { get { return instance; } }
public IComOpenFileBrowser openFileBrowser { get; private set; }
MainWindow mainWindow;
public Database CurrentDatabase;
private Manager()
{
openFileBrowser = new IComOpenFileBrowser(SaveFileDialog);
CurrentDatabase = new Database();
mainWindow = new MainWindow();
mainWindow.DataContext = this;
mainWindow.Show();
}
private void SaveFileDialog()
{
var sfd = new SaveFileDialog();
sfd.Title = "Choose a filename and a directory to store the new database";
sfd.Filter = "SQLite Database (*.sqlite)|*.sqlite";
if (sfd.ShowDialog() == true)
{
CurrentDatabase.FullFilePath = sfd.FileName;
}
}
}
database.cs
public class Database : INotifyPropertyChanged
{
private string _FullFilePath;
public string FullFilePath {
get {
return _FullFilePath;
}
set { _FullFilePath = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The thing is when I click on the Button, the SaveFileDialog appears, but when I type in a name and click OK, the Filename does not appear in the Textbox.
BUT: When I set mainWindow.txtPath.DataContext = CurrentDatabase; and delete the "CurrentDatabase." from the XAML code then it works.
I thought that it's correct to set the DataContext of the whole window to the manager class and then access the subclass in XAML by ..{Binding Path= CurrentDatabase.FullFilePath}..
But obviously thats not the case.
Can anybody tell my what I'm misunderstanding here?
Related
C#, WPF.
I am trying to implement a UserControl with elements bound to properties in an object hierarchy. I have been using this as a reference.
I have created the following minimal example. It implements three instances of the UserControl, with the textbox in each case representing a filename. A dependency property is used to permit binding. Although it executes without errors, the textboxes are blank. They should contain "test1", "test2" and "test3". What am I missing?
Main window:
<Window x:Class="CustomControlTest.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:mycontrols="clr-namespace:MyControls"
mc:Ignorable="d"
Title="MainWindow" Height="100" Width="800">
<Grid>
<StackPanel Orientation="Vertical" DataContext="{Binding ElementName=parent}">
<mycontrols:DataFileControl x:Name="Ctrl1" FName="{Binding Path=project.File1.Filename}"/>
<mycontrols:DataFileControl x:Name="Ctrl2" FName="{Binding Path=project.File2.Filename}"/>
<mycontrols:DataFileControl x:Name="Ctrl3" FName="{Binding Path=project.File3.Filename}"/>
</StackPanel>
</Grid>
</Window>
namespace CustomControlTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Project project = new Project();
public MainWindow()
{
InitializeComponent();
project.File1.Filename = "test1";
project.File2.Filename = "test2";
project.File3.Filename = "test3";
}
}
public class Project : INotifyPropertyChanged
{
public DataFile File1 { get; set; } = new DataFile();
public DataFile File2 { get; set; } = new DataFile();
public DataFile File3 { get; set; } = new DataFile();
public event PropertyChangedEventHandler PropertyChanged;
}
public class DataFile : INotifyPropertyChanged
{
public string Filename { get; set; } = "";
public event PropertyChangedEventHandler PropertyChanged;
}
}
UserControl:
<UserControl x:Class="MyControls.DataFileControl"
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="40"
d:DesignWidth="800"
Name="TheCtrl">
<Grid Height="20">
<TextBox Name="filenameTextBox" Margin="5,0,5,0" Text="{Binding ElementName=TheCtrl, Path=FName, Mode=TwoWay}"/>
</Grid>
</UserControl>
namespace MyControls
{
public partial class DataFileControl : System.Windows.Controls.UserControl
{
public string FName
{
get { return (string)GetValue(FNameProperty); }
set { SetValue(FNameProperty, value); }
}
public static readonly DependencyProperty FNameProperty =
DependencyProperty.Register("FName", typeof(string), typeof(DataFileControl), new PropertyMetadata(null));
public DataFileControl()
{
InitializeComponent();
}
}
}
The expression
FName="{Binding Path=project.File1.Filename}"
requires a public property named project in the current DataContext, which you have not set. You should have noticed a data binding error message in the Output Window in Visual Studio when you debug the application.
Change it to
public Project project { get; } = new Project();
public MainWindow()
{
InitializeComponent();
project.File1.Filename = "test1";
project.File2.Filename = "test2";
project.File3.Filename = "test3";
DataContext = this;
}
Alternatively, use the Project instance as DataContext (and thus make it a view model)
private readonly Project project = new Project();
public MainWindow()
{
InitializeComponent();
project.File1.Filename = "test1";
project.File2.Filename = "test2";
project.File3.Filename = "test3";
DataContext = project;
}
and change the binding expressions to
FName="{Binding File1.Filename}"
I'm new to C# and UWP. After many days of reading official microsoft docks and stackoverflow I still cannot understand what am I doing wrong.
Here is extremely simple test app.
I am trying to change grid's background on click. When application starts the grid's background is bg-1.jpg as expected. After click the value of property PicturePath have changed, but Grid's background have not.
What am I missing?
Here is a simple xaml page
<Page
x:Class="Test.Views.StartPage"
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"
mc:Ignorable="d">
<Grid PointerPressed="Grid_PointerPressed">
<Grid.Background>
<ImageBrush ImageSource="{Binding PicturePath, Mode=OneWay}" />
</Grid.Background>
</Grid>
</Page>
Code behind
public sealed partial class StartPage : Page
{
public StartPage()
{
this.InitializeComponent();
DataContext = new {
TestBackgroundClass.getInstance().PicturePath,
};
}
private void Grid_PointerPressed(object sender, PointerRoutedEventArgs e)
{
TestBackgroundClass.getInstance().PicturePath = "ms-appx:///Assets/bg-2.jpg";
}
}
and a singleton class
public class TestBackgroundClass : INotifyPropertyChanged
{
private static TestBackgroundClass instance;
private string _picturePath { get; set; }
public string PicturePath
{
get
{
return _picturePath;
}
set
{
_picturePath = value;
NotifyPropertyChanged();
}
}
public TestBackgroundClass()
{
_picturePath = "ms-appx:///Assets/bg-1.jpg"
}
public static TestBackgroundClass getInstance()
{
if (instance == null) instance = new TestBackgroundClass();
return instance;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I have a window and an usercontrol used within this window.
And I have done the following:
Bound the window property to window's viewmodel property.
Bound the usercontrol property to usercontrol's viewmodel property.
Bound the usercontrol's property to window's viewmodel property.
In my opinion, I think if I set window property by specific value, usercontrol's viewmodel's property will be set to the same value.
But usercontrol's viewmodel's property was not set to the same value.
Here is whole codes.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Window1 w = new Window1();
w.Mode = RegisterMode.Update;
w.Show();
}
}
Window1.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMTest2" x:Name="window" x:Class="MVVMTest2.Window1"
Title="Window1" Height="300" Width="300">
<Window.DataContext>
<local:WindowViewModel></local:WindowViewModel>
</Window.DataContext>
<Grid>
<local:UserControl1 x:Name="uc1" HorizontalAlignment="Left" Height="100" Margin="77,116,0,0" VerticalAlignment="Top" Width="100" Mode="{Binding DataContext.Mode, ElementName=window, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="156,43,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
</Window>
Window1.xaml.cs
public partial class Window1 : Window
{
public RegisterMode Mode
{
get { return (RegisterMode)GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
// Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ModeProperty =
DependencyProperty.Register("Mode", typeof(RegisterMode), typeof(Window1), new PropertyMetadata(RegisterMode.None));
public Window1()
{
InitializeComponent();
Binding modeBinding = new Binding();
modeBinding.Source = this.DataContext;
modeBinding.Path = new PropertyPath("Mode");
modeBinding.Mode = BindingMode.TwoWay;
modeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
this.SetBinding(Window1.ModeProperty, modeBinding);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show((uc1.DataContext as UCViewModel).Mode.ToString());
}
}
WindowViewModel.cs
public class WindowViewModel : INotifyPropertyChanged
{
RegisterMode mode = RegisterMode.None;
public event PropertyChangedEventHandler PropertyChanged;
public RegisterMode Mode
{
get { return this.mode; }
set
{
this.mode = value;
RaisePropertyChanged("Mode");
}
}
void RaisePropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
UserControl1.xaml
<UserControl x:Class="MVVMTest2.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MVVMTest2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<local:UCViewModel></local:UCViewModel>
</UserControl.DataContext>
<Grid>
</Grid>
</UserControl>
UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public RegisterMode Mode
{
get { return (RegisterMode)GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
// Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ModeProperty =
DependencyProperty.Register("Mode", typeof(RegisterMode), typeof(UserControl1), new PropertyMetadata(RegisterMode.None));
public UserControl1()
{
InitializeComponent();
Binding modeBinding = new Binding();
modeBinding.Source = this.DataContext;
modeBinding.Path = new PropertyPath("Mode");
modeBinding.Mode = BindingMode.TwoWay;
modeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
this.SetBinding(UserControl1.ModeProperty, modeBinding);
}
}
UCViewModel.cs
public class UCViewModel : INotifyPropertyChanged
{
RegisterMode mode = RegisterMode.None;
public event PropertyChangedEventHandler PropertyChanged;
public RegisterMode Mode
{
get { return this.mode; }
set
{
this.mode = value;
RaisePropertyChanged("Mode");
}
}
void RaisePropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
What point am I doing wrong?
Please let me know about it.
Try this solution using composition between your ViewModels.
Window1.xaml
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMTest2" x:Name="window" x:Class="MVVMTest2.Window1"
Title="Window1" Height="300" Width="300"
Mode={Binding UcViewModel.Mode}> <!-- Added this binding -->
<!-- MOVED THIS TO THE CODE-BEHIND
<Window.DataContext>
<local:WindowViewModel></local:WindowViewModel>
</Window.DataContext>
-->
<Grid>
<local:UserControl1 x:Name="uc1" HorizontalAlignment="Left" Height="100" Margin="77,116,0,0" VerticalAlignment="Top" Width="100"
Mode="{Binding UcViewModel.Mode}"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="156,43,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
</Window>
Window1.xaml.cs
public partial class Window1 : Window
{
public RegisterMode Mode
{
get { return (RegisterMode)GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
// Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ModeProperty =
DependencyProperty.Register("Mode", typeof(RegisterMode), typeof(Window1), new PropertyMetadata(RegisterMode.None));
public Window1()
{
InitializeComponent();
/* THIS IS NOT NEEDED
Binding modeBinding = new Binding();
modeBinding.Source = this.DataContext;
modeBinding.Path = new PropertyPath("Mode");
modeBinding.Mode = BindingMode.TwoWay;
modeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
this.SetBinding(Window1.ModeProperty, modeBinding)
*/
// Use the following instead:
this.DataContext = new WindowViewModel();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show((uc1.DataContext as UCViewModel).Mode.ToString());
}
}
UserControl1.xaml
<UserControl x:Class="MVVMTest2.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MVVMTest2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<!-- THIS PART IS NOT NEEDED
<UserControl.DataContext>
<local:UCViewModel></local:UCViewModel>
</UserControl.DataContext>
-->
<Grid>
</Grid>
</UserControl>
UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public RegisterMode Mode
{
get { return (RegisterMode)GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
// Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ModeProperty =
DependencyProperty.Register("Mode", typeof(RegisterMode), typeof(UserControl1), new PropertyMetadata(RegisterMode.None));
public UserControl1()
{
InitializeComponent();
/* THIS IS NOT NEEDED
Binding modeBinding = new Binding();
modeBinding.Source = this.DataContext;
modeBinding.Path = new PropertyPath("Mode");
modeBinding.Mode = BindingMode.TwoWay;
modeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
this.SetBinding(UserControl1.ModeProperty, modeBinding);
*/
}
}
WindowViewModel.cs
public class WindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/* Remove this:
RegisterMode mode = RegisterMode.None;
public RegisterMode Mode
{
get { return this.mode; }
set
{
this.mode = value;
RaisePropertyChanged("Mode");
}
}
*/
// Use composition instead
UCViewModel _ucViewModel = null;
public UCViewModel UcViewModel
{
get
{
if (_ucViewModel == null)
{
_ucViewModel = new UCViewModel();
}
return _ucViewModel;
}
}
void RaisePropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And one more thing, you might want to rename the ViewModel property Mode to another name like RegMode. The reason is that it's confusing in the XAML code during binding.
From this:
{Binding Mode, Mode=TwoWay}
Or, this:
{Binding Path=Mode, Mode=TwoWay}
To this less confusing one:
{Binding RegMode, Mode=TwoWay}
I have a problem to bind a subclass to my XML textbox, I followed this post to do it, but it does not work without using a static class. Is there a way to do without using a static class?
I followed this post as reference.
Binding textbox values to a model in wpf
My code is:
public class Model:INotifyPropertyChanged{
public event PropertyChangedEventHandler PropertyChanged;
private string title;
public string Title{
get {
return title;
}
set {
if (tilte!= value) {
tilte= value;
NotifyPropertyChanged();
}
}
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") {
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class ViewModel{
public Model modelObj;
public ViewModel(){
modelObj= new Model();
this.DataContext = modelObj;
modelObj.Title = "New title"; // <--- this don't update xml
}
}
<Page
x:Class="AppTest.Demo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:m ="using:Models"
xmlns:vm ="using:ViewModels"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.DataContext>
<m:Model></m:Model>
</Page.DataContext>
<Grid>
<TextBlock Text="{Binding Path=Title}"/>
</Grid>
</Page>
You could set your view model as the data context and bind to Model.Title.
Update
This works:
<Page x:Class="WpfApplication8.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:m ="using:Models"
xmlns:vm="clr-namespace:WpfApplication8.ViewModels">
<Page.DataContext>
<vm:ViewModel/>
</Page.DataContext>
<Grid>
<TextBlock Text="{Binding ModelObj.Title, TargetNullValue='null', FallbackValue='fallback'}"/>
</Grid>
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Model : BindableBase
{
private string title;
public string Title
{
get
{
return title;
}
set
{
if (title != value)
{
title = value;
NotifyPropertyChanged();
}
}
}
}
public class ViewModel : BindableBase
{
private Model modelObj;
public Model ModelObj
{
get
{
return modelObj;
}
set
{
modelObj = value;
NotifyPropertyChanged();
}
}
public ViewModel()
{
ModelObj = new Model();
ModelObj.Title = "New title";
}
}
You should set the Title property of the instance of the Model class that you set as the DataContext of the Page:
<Page.DataContext>
<m:Model Title="New title"></m:Model>
</Page.DataContext>
Or:
<Page.DataContext>
<m:ViewModel />
</Page.DataContext>
<Grid>
<TextBlock Text="{Binding Path=modelObj.Title}"/>
</Grid>
Also, you don't set the DataContext property of a view model. You set the DataContext property of a view to an instance of a view model.
Edit:
modelObj must be a public property (and not a field) in order for you to be able to bind to it:
public Model modelObj { get; set; }
I am trying to implement Help functionality for my wpf application which is following the MVVM pattern. I have my help file present, which contains many pages according to the application. Now I need to integrate this into my application.
Here are my requirements:
Pressing F1 opens a certain page in the help file depending on the view model. For this, I guess, I need to bind the F1 command to my view model. How do we bind keys in views?
Pressing F1 on a text field opens help for that text field. I think it will be the same as requirement 1. But the problem here is how will I know that a certain text field, button, or radio button is selected?
Listen for the key in the view (or a base class of the view) and call execute on a HelpCommand on the DataContext.
Pass the control that has focus (or its id, or tag, ...) as an argument to the HelpCommand.
Alternative way to find the focussed control by using the FocusManager
Here is an example:
ContextHelp C#:
public static class ContextHelp
{
public static readonly DependencyProperty KeywordProperty =
DependencyProperty.RegisterAttached(
"Keyword",
typeof(string),
typeof(ContextHelp));
public static void SetKeyword(UIElement target, string value)
{
target.SetValue(KeywordProperty, value);
}
public static string GetKeyword(UIElement target)
{
return (string)target.GetValue(KeywordProperty);
}
}
ViewBase:
public abstract class ViewBase : UserControl
{
public ViewBase()
{
this.KeyUp += ViewBase_KeyUp;
this.GotFocus += ViewBase_GotFocus;
}
void ViewBase_GotFocus(object sender, RoutedEventArgs e)
{
FocusManager.SetIsFocusScope(this, true);
}
void ViewBase_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.F1)
{
var viewModel = this.DataContext as ViewModelBase;
if (viewModel != null)
{
var helpTopic = "Index";
var focusedElement =
FocusManager.GetFocusedElement(this) as FrameworkElement;
if (focusedElement != null)
{
var keyword = ContextHelp.GetKeyword(focusedElement);
if (!String.IsNullOrWhiteSpace(keyword))
{
helpTopic = keyword;
}
}
viewModel.HelpCommand.Execute(helpTopic);
}
}
}
}
ViewModelBase:
public abstract class ViewModelBase: INotifyPropertyChanged
{
public ICommand HelpCommand { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName="")
{
var p = PropertyChanged;
if (p != null)
{
p(this, new PropertyChangedEventArgs(propertyName));
}
}
}
AViewModel:
class AViewModel : ViewModelBase
{
public AViewModel()
{
HelpCommand = new RelayCommand(HelpCommandExecuted, (p)=>true);
}
private void HelpCommandExecuted(object parameter)
{
var topic = parameter as string;
if (!String.IsNullOrWhiteSpace(topic))
{
HelpText = String.Format("Information on the interesting topic: {0}.", topic);
}
}
private string _helpText;
public string HelpText
{
get { return _helpText; }
private set
{
if (_helpText != value)
{
_helpText = value;
OnPropertyChanged();
}
}
}
}
AView C#:
public partial class AView : ViewBase
{
public AView()
{
InitializeComponent();
}
}
AView XAML:
<local:ViewBase x:Class="WpfApplication2.AView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Label Content="{Binding HelpText}" Margin="10,254,10,0" VerticalAlignment="Top" Height="36"/>
<Button local:ContextHelp.Keyword="Button Info" Content="Button" HorizontalAlignment="Left" Margin="192,32,0,0" VerticalAlignment="Top" Width="75"/>
<TextBox local:ContextHelp.Keyword="TextBox Info" HorizontalAlignment="Left" Height="23" Margin="29,32,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
<CheckBox local:ContextHelp.Keyword="CheckBox Info" Content="CheckBox" HorizontalAlignment="Left" Margin="29,80,0,0" VerticalAlignment="Top"/>
<ComboBox local:ContextHelp.Keyword="ComboBox Info" HorizontalAlignment="Left" Margin="138,80,0,0" VerticalAlignment="Top" Width="120"/>
</Grid>
</local:ViewBase>
MainWindow XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2" x:Class="WpfApplication2.MainWindow"
Title="MainWindow" Height="700" Width="500">
<Grid x:Name="ViewPlaceholder">
</Grid>
</Window>
MainWindow C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var view = new AView();
var viewModel = new AViewModel();
view.DataContext = viewModel;
ViewPlaceholder.Children.Clear();
ViewPlaceholder.Children.Add(view);
}
}