I have an application which has CarViewModel + view (UserControl).
What I want to achieve is to change the style of brushes when the bound DataContext Car.Status changes.
I found out how to change the brushes (in code behind of the view):
private void LoadThemeResources(bool isPrepareMode)
{
if (isPrepareMode)
{
Uri themeUri = new Uri(#"/../Resources/MyBrushes.Light.xaml", UriKind.Relative);
ResourceDictionary themeDictionary = Application.LoadComponent(themeUri) as ResourceDictionary;
this.Resources.MergedDictionaries.Add(themeDictionary);
}
else
{
this.Resources.MergedDictionaries.Clear();
}
}
By default the application and everthing has a dark theme spread over multiple files. This MyBrushes.Light overwrites some of those.
But I have no clue how I can execute the LoadThemeResources function based on a property change in the ViewModel in a MVVM friendly way.
I can do in the code behind of the view:
var vm = (CarViewModel) DataContext;
vm.Car.PropertyChanged += HandleStatusChanged;
But this is a tight coupling between View and ViewModel.
I can also do it via Messenger (From MVVM Light), but that gets broadcasted throughout the whole application and seems overkill.
Is there an other way? Or preferred way?
I would prepare some attached property (used on UserControl). Bind that property to your view-model and add code logic of LoadThemeResources in the property changed callback, something like this:
public static class ThemeService {
public static DependencyProperty IsPrepareModeProperty =
DependencyProperty.RegisterAttached("IsPrepareMode", typeof(bool), typeof(ThemeService),
new PropertyMetadata(isPrepareModeChanged));
public static bool GetIsPrepareMode(UserControl e){
return (bool) e.GetValue(IsPrepareModeProperty);
}
public static void SetIsPrepareMode(UserControl e, bool value){
e.SetValue(IsPrepareModeProperty, value);
}
static void isPrepareModeChanged(object sender, DependencyPropertyChangedEventArgs e){
var u = sender as UserControl;
u.LoadThemeResources((bool)e.NewValue);
}
}
//you need some public method of LoadThemeResources
public void LoadThemeResources(bool isPrepareMode) {
//...
}
Usage in XAML:
<UserControl ...
local:ThemeService.IsPrepareMode="{Binding Car.Status}">
<!-- ... -->
</UserControl>
You can also declare a normal DependencyProperty for your UserControl's class and use that instead of the attached property (the usage is just the same).
You could bind to a property on your ViewModel, and use an IValueConverter in your View to turn that property (whether boolean, status enumeration, whatever) into a Brush to be used.
That is, load the theme/resources in the converter (a deliberate bridge between View and ViewModel) so that your View gets the Brush it wants and your ViewModel only has to expose the 'important' information (the bits that help decide what brush to load). The decision logic is all in the converter.
Related
In my View I'm using a component (custom control), which provides some functions. I want to invoke one of them when my ViewModel receives an event it is subscribed to.
I want to do this as cleanly as possible, since there might be more functions I would be using this way.
I know I can create a variable like "InvokeFunctionA", bind to this variable and create OnChange method in my View which will invoke the corresponding function. But it's quite a lot of code required just to invoke a single function. And an extra variable, which seems quite unnesessary, too.
Is there a better way to do this? Like, maybe a View can pass some kind of a handler function to ViewModel which will do the work? I've made quite a lot of research but haven't yet found anything that suits my problem. Or maybe I'm missing something obvious?
[ edit ]
Haukinger solution works for now (done this way: https://blog.machinezoo.com/expose-wpf-control-to-view-model-iii ), but I don't think it's the cleanest solution (Instead of providing access to a few functions, I'm exposing whole control to the ViewModel).
In a perfect MVVM-world (as you are asking for a clean solution), the ViewModel does not call anything that is located in the view (neither directly nor indirectly). I'd approach the problem like this:
If 'component' is NOT a usercontrol, try moving it to the ViewModel and use bindings or commands in the view to operate your 'component'.
If 'component' is a usercontrol, give 'component' a dependency property and fill it via a binding with your property of the ViewModel. Inside of 'compontent' you can register value change callback of your dependency property to start your work. <local:UserControlComponent MyDependencyProperty="{Binding PropertyInViewModel}" />
As a last resort:
You could add a C# event to the viewmodel and handle it in your code-behind inside the view.
Instead of an event, you could alternatively use IObservable pattern (https://learn.microsoft.com/en-us/dotnet/api/system.iobservable-1?view=netframework-4.8, https://github.com/dotnet/reactive)
For completeness sake a no-go option: Prism has an EventAggregator that can be used for loose communication. I've had to remove the usage of EventAggregator from a rather big App, because it was not maintainable any more.
Expose a dependency property in your view whose type is the provided interface, bind it to a property on your view model, then call the method on the interface on the view model property from the view model.
To clarify, I don't mean to expose the component itself, rather an interface that contains exactly one method. The view has to have a private class that implements the interface and routes to the actual component, as well as converting arguments and results so that types belonging to the components need not be present in the interface.
But I'm with sa.he in that this whole situation should be avoided in the first place. It may not be possible, depending on the third party components used, though.
Yes, invoking view's methods from VM is very much against pure MVVM and there's not going to be a 'clean' solution.
But it can be done at least half decently. You would need to create a special attached property (or behavior, but property seems to be a better choice in this scenario) and an ICommand property in VM, then bind the AP to the property with OneWayToSource binding and use command invocation in VM. It would still be a lot of code, but once it's done, you would only need to create new properties in the VM.
Below is some code that I wrote, consider it as a starting point, you can add support for command parameters and converters.
public class MethodDelegation : DependencyObject
{
public static readonly DependencyProperty CommandDelegatesProperty =
DependencyProperty.RegisterAttached("CommandDelegatesInternal", typeof(CommandDelegatesCollection), typeof(MethodDelegation), new PropertyMetadata(null));
private MethodDelegation() { }
public static CommandDelegatesCollection GetCommandDelegates(DependencyObject obj)
{
if (obj.GetValue(CommandDelegatesProperty) is null)
{
SetCommandDelegates(obj, new CommandDelegatesCollection(obj));
}
return (CommandDelegatesCollection)obj.GetValue(CommandDelegatesProperty);
}
public static void SetCommandDelegates(DependencyObject obj, CommandDelegatesCollection value)
{
obj.SetValue(CommandDelegatesProperty, value);
}
}
public class CommandDelegatesCollection : FreezableCollection<CommandDelegate>
{
public CommandDelegatesCollection()
{
}
public CommandDelegatesCollection(DependencyObject targetObject)
{
TargetObject = targetObject;
((INotifyCollectionChanged)this).CollectionChanged += UpdateDelegatesTargetObjects;
}
public DependencyObject TargetObject { get; }
protected override Freezable CreateInstanceCore()
{
return new CommandDelegatesCollection();
}
private void UpdateDelegatesTargetObjects(object sender, NotifyCollectionChangedEventArgs e)
{
foreach (CommandDelegate commandDelegate in e?.NewItems ?? Array.Empty<CommandDelegate>())
{
commandDelegate.TargetObject = TargetObject;
}
}
}
public class CommandDelegate : Freezable
{
public static readonly DependencyProperty MethodNameProperty =
DependencyProperty.Register("MethodName", typeof(string), typeof(CommandDelegate), new PropertyMetadata(string.Empty, MethodName_Changed));
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandDelegate), new PropertyMetadata(null));
public static readonly DependencyProperty TargetObjectProperty =
DependencyProperty.Register("TargetObject", typeof(DependencyObject), typeof(CommandDelegate), new PropertyMetadata(null, TargetObject_Changed));
private MethodInfo _method;
public string MethodName
{
get { return (string)GetValue(MethodNameProperty); }
set { SetValue(MethodNameProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public DependencyObject TargetObject
{
get { return (DependencyObject)GetValue(TargetObjectProperty); }
set { SetValue(TargetObjectProperty, value); }
}
protected override Freezable CreateInstanceCore()
{
return new CommandDelegate();
}
private static void MethodName_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var del = (CommandDelegate)d;
del.UpdateMethod();
del.UpdateCommand();
}
private static void TargetObject_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var del = (CommandDelegate)d;
del.UpdateMethod();
del.UpdateCommand();
}
private void UpdateMethod()
{
_method = TargetObject?.GetType()?.GetMethod(MethodName);
}
private void UpdateCommand()
{
Command = new RelayCommand(() => _method.Invoke(TargetObject, Array.Empty<object>()));
}
}
The XAML usage is as follows:
<TextBox>
<l:MethodDelegation.CommandDelegates>
<l:CommandDelegate MethodName="Focus"
Command="{Binding TestCommand, Mode=OneWayToSource}" />
</l:MethodDelegation.CommandDelegates>
</TextBox>
Bubble your event upwards. Have your VM publish some event of its own. Your V can subscribe to it (if it wishes).
The downside is that you'll need codebehind, where ideally a V should be XAML-only as far as possible. The upside is that your VM remains quite aloof (i.e. it's not dependent on any specific controls used by the V). It says "something has happened worthy of note", but it doesn't assume either that (a) anyone is particularly listening, or (b) it leaves it to the listener (in your case, the V) to decide exactly what to action to take (i.e. how to change the UI).
It's a perennial problem - how does a VM cause a V to update somehow, and as far as I can tell it is still something to be debated.
The mechanism above, I've got a vague recollection that Prism itself might include something similar. I'm fairly sure it uses something akin to INotifyPropertyChanged (i.e. some interface or other) rather than an "event" as we might understand it just from a working knowledge of .net. You might even be able to use this mechanism to dispense with codebehind altogether. The downside of using Prism in the first place is its bulk, but if you're already using it anyway...
It's for you to decide how clean this is. I decided that a bit of codebehind was preferable to the VM meddling directly with the UI.
I am rather new to MVVC/wpf, having mostly worked with winforms.
What I want to accomplish is dynamic databinding without using code behind in WPF. The user interface consists of a devexpress grid and a couple of buttons. Each button press loads an object list and presents the objects in the grid. The lists contain different object types depending on the button pressed. For this example I have two classes to present: FatCat and FatDog.
In winforms this works:
private void button1_Click(object sender, EventArgs e)
{
((GridView)gridCtrl.MainView).Columns.Clear();
gridCtrl.DataSource = new BindingSource(itsModel.GetAll<FatDog>(), null);
}
private void button2_Click(object sender, EventArgs e)
{
((GridView)gridCtrl.MainView).Columns.Clear();
gridCtrl.DataSource = new BindingSource(itsModel.GetAll<FatCat>(), null);
}
I have configured the grid to create columns dynamically, so everything just works. itsModel is of type CatClientModel.
In wpf I have defined the DataContext to be CatClientModel.
What should I use for ItemsSource in the grid to achieve the same behaviour as my winforms solution?
dxg:GridControl ItemsSource="{Binding SomeDynamicList}"
In other words, what should SomeDynamicList be in the code above? Or am I going about this the wrong way?
I am, as I stated, using the DevExpress wpf grid control, but the question ought to be general and apply to any control presenting object lists.
In other words, what should SomeDynamicList be in the code above?
SomeDynamicList should be an ObservableCollection<T> property to which you can add any objects of type T that you want to display in the GridControl.
Set the DataContext of the GridControl, or any of its parent elements, to an instance of a class where this property is defined:
public class CatClientModel
{
public ObservableCollection<Animal> SomeDynamicList { get; } = new ObservableCollection<Animal>();
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new CatClientModel();
}
}
Ok. But the thing is that the ObservableCollection contains different types. Unfortunately there is no feasible class to inherit from. I want to bind to either ObservableCollection or ObservableCollection depending on which button was pressed
Switch the DataContext then, or change the property into an IEnumerable and set it to a new collection each time the button is clicked. This requires you to implement the INotifyPropertyChanged interface in your view model
private System.Collections.IEnumerable _collection;
public System.Collections.IEnumerable MyProperty
{
get { return _collection; }
set { _collection = value; OnPropertyChanged(); }
}
If you want to use XAML to define which data sources your code maps to for each grid that is possible. That does require at least some method of MVVM manager either prism or mvvmlight to connect the view model to the view.
so if you do go the MVVM model route, the Model would contain a description for each of your grids like this:
public BulkObservableCollection<icd10facet> FacetList
{
get { return this._facets; }
set { SetProperty(ref this._facets, value); }
}
public INotifyTaskCompletion<BulkObservableCollection<PetsConvert>> ConceptList
{
get { return this._concept; }
set
{
SetProperty(ref this._concept, value);
}
}
In the XAML for your code the grid woud bind to the grid defined by ConceptList in this way:
ItemsSource="{Binding ConceptList.Result}"
this answer does NOT address how to wire up Prism 6.0 for example to use a view model but for examples see:
https://github.com/PrismLibrary/Prism
Which contains documentation and starter code. Keep in mind that there is not any specific reason that putting code in the code behind for the view is a problem, first solve the problem and then refactor if separation of concerns is an issue for you.
Using this technique you can bind each grid to its own data source. In the MVVM space buttons and other things use a commanding model to communicate with the view model.
<Button Content="Load Rule Data" Width="100" Height="40" HorizontalAlignment="Left" Margin="5px" Command="{Binding LoadRuleData }"/>
this requires defining a command delegate in the viewmodel for LoadRuleData
public DelegateCommand LoadRuleData { get; private set; }
and then (usually in the constructor) wire the DelegateCommand to the method that is going to do the work.
this.LoadRuleData = new DelegateCommand(this.loadRules);
I interested in useful user control Monitor from the external library in my MVVM style project.
All looks fine...but this control has simple (not a dependency)read only property (IList<ILogSource>), which I need to fill.
After thinking a little I decided to wrap this Monitor control with other control MonitorWrap:
<UserControl
x:Class="Prj.CustomControls.MonitorWrap"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://catel.codeplex.com"
xmlns:uc="clr-namespace:UsefulControll;assembly=UsefulControll">
<uc:Monitor x:Name="Monitor" />
</UserControl>
and at code behind i create dependency property:
public partial class MonitorWrap : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="MonitorWrap"/> class.
/// </summary>
public MonitorWrap()
{
InitializeComponent();
DataContextChanged += (sender, args) =>
{
//correct ViewModel sets to DataContext
};
}
public IList<ILogSource> LogSources
{
get { return (IList<ILogSource>)GetValue(LogSourcesProperty); }
set { SetValue(LogSourcesProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LogSourcesProperty =
DependencyProperty.Register("LogSources", typeof(IList<ILogSource>), typeof(MonitorWrap), new PropertyMetadata(null,ChangeCallback));
private static void ChangeCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var logControlPanelView = d as MonitorWrap;
//add elements from LogSources to readonly collection property.
}
}
next step at parent xml:
<customControls:MonitorWrap LogSources="{Binding Sources}"/>
I expect that I'll fill collection at Change Callback method but it doesn't work.
So questions:
am I going the right way to fill read-only collection?
what's wrong? why ChangeCallback method won't work.
P.s.
i'm using MVVM framework and DataContext sets correct(lambda expression in MonitorWrap constructor works fine).
ViewModel implements INotifyPropertyChanged and code
protected override void OnPropertyChanged(AdvancedPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.PropertyName == "Sources")
{
//works fine on property changed
}
}
works fine too.
IList<ILogSource> looks suspiciously to me like you might be asking more of C#'s support for covariance than you're going to get.
The type of the viewmodel property must be assignable to IList<ILogSource>, and the rules for that are fairly strict.
Fundamentally, ObservableCollection<ILogSource> is IList<ILogSource> but it is not IList<ILogSource_inherited_Class>. It's not just the binding that can't do that. You can copy items from one to the other, but you can't cast. All you want to do is copy, but the typesystem doesn't know that -- it just sees you trying to do the assignment.
What should work is making the dependency property of type IEnumerable<ILogSource> (see fiddle). All you need to do is copy the items out of it, so that's sufficient. Don't tell the compiler you need everything IList<ILogSource> can do, if all you need is IEnumerable<ILogSource>.
I am trying to solve this issue for so many hours:
I have user custom control of grid named NewMazeGrid and I want to use it as a control in MainWindow. MainWindow contains MazeViewModel(mazeVM member).
I'm trying to set the values of the grid, when the property MazeViewModel:MySingleplay changes.
(I'm using the INotifyPropertyChanged for it, and it works perfectly fine. I guess, the problem is in the final binding)
The code:
This is the property MazeViewModel:MySingleplay getter:
public string MySingleplay
{
get
{
if (myModel.MySingleplay == null)
{
return "";
} else
{
return myModel.MySingleplay.ToString();//works perfect
}
}
}
this is the NewMazeGrid.xaml.cs:
namespace VisualClient.View.controls
{
public partial class NewMazeGrid : UserControl
{
private MazePresentation myMaze;
private string order; //dont really use it
//Register Dependency Property
public static readonly DependencyProperty orderDependency =
DependencyProperty.Register("Order", typeof(string), typeof(NewMazeGrid));
public NewMazeGrid()
{
myMaze = new MazePresentation();
InitializeComponent();
DataContext = this;
lst.ItemsSource = myMaze.MazePuzzleLists;
}
public string Order
{
get
{
return (string)GetValue(orderDependency);
}
set
{
SetValue(orderDependency, value);
myMaze.setPresentation(value); //(parsing string into matrix)
}
}
}
}
this is the MainWindow.xaml.cs:
public partial class MainWindow : Window
{
private MazeViewModel mazeVM;
public MainWindow()
{
InitializeComponent();
mazeVM = new MazeViewModel(new ClientMazeModel(new TCPClientConnection()));
DataContext = mazeVM;
mazeVM.connectToServer();
}
private void bu_Click(object sender, RoutedEventArgs e)
{
bool isC = mazeVM.isConnected();
mazeVM.openSingleplayGame("NewMaze");//works perfect
}
this is the MainWindow.xaml:
<Window x:Class="VisualClient.View.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:Controls ="clr-namespace:VisualClient.View.controls"
xmlns:vm ="clr-namespace:VisualClient.ViewModel"
xmlns:local="clr-namespace:VisualClient.View"
mc:Ignorable="d"
Title="Main Window" Height="350" Width="525" MinWidth="900" MinHeight="600">
<WrapPanel >
<Button Name ="bu" Content="Click_Me" Click="bu_Click"/>
<Grid Name="myGrid">
<Controls:NewMazeGrid Order="{Binding MySingleplay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</WrapPanel>
</Window>
I get this error on the binding line: Value cannot be null.
To sum:
It initialize fine the window in the ctor, but when the property changes it does not get into the Order property setter. therefor my grid never changes.
What should be the right syntax for binding in this case? how do I bind it to the right property?
Folders hierarchy explorer
WPF may not call the CLR wrapper of a dependency property, but just directly call the GetValue and SetValue methods of the underlying DependencyObject. This is why there should not be any logic except the GetValue and SetValue calls.
This is explained in XAML Loading and Dependency Properties:
Because the current WPF implementation of the XAML processor behavior
for property setting bypasses the wrappers entirely, you should not
put any additional logic into the set definitions of the wrapper for
your custom dependency property. If you put such logic in the set
definition, then the logic will not be executed when the property is
set in XAML rather than in code.
Similarly, other aspects of the XAML processor that obtain property
values from XAML processing also use GetValue rather than using the
wrapper. Therefore, you should also avoid any additional
implementation in the get definition beyond the GetValue call.
To get notified about property value changes, you can register a PropertyChangedCallback by property metadata. Note also that there is a naming convention for DependencyProperty fields. Yours should be called OrderProperty:
public static readonly DependencyProperty OrderProperty =
DependencyProperty.Register(
"Order", typeof(string), typeof(NewMazeGrid),
new PropertyMetadata(OnOrderChanged));
public string Order
{
get { return (string)GetValue(OrderProperty); }
set { SetValue(OrderProperty, value); }
}
private static void OnOrderChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((NewMazeGrid)obj).myMaze.setPresentation((string)e.NewValue);
}
Besides that, you must not set
DataContext = this;
in the constructor of NewMazeGrid. This effectively prevents inheriting the DataContext from the parent window, so that {Binding MySingleplay} won't work. Except under special circumstances you should never explicitly set a UserControl's DataContext.
So, remove the DataContext assignment from the constructor:
public NewMazeGrid()
{
myMaze = new MazePresentation();
InitializeComponent();
lst.ItemsSource = myMaze.MazePuzzleLists;
}
That said, there is also no need to set UpdateSourceTrigger=PropertyChanged on a one-way binding. It only has an effect in two-way (or one-way-to-source) bindings:
<Controls:NewMazeGrid Order="{Binding MySingleplay}"/>
Well, I am doing a small project and I found it wasn't necessary to implemente a full MVVM.
I am trying to bind some properties in code behind, but cannot manage to make it works.
The point is the use of DependencyProperties and Binding in code behind.
I tried to follow these links and questions in SO:
Bind Dependency Property in codebehind WPF
How to: Create a Binding in Code
Bind Dependency Property, defined in Code-Behind, through Xaml to a Property in the DataContext of a UserControl
But they are related to MVVM or at least I cannot adapt the code in my case.
The example should be very simple.
MainWindow.xaml
<Label Name="_lblCurrentPath"
Style="{StaticResource CustomPathLabel}"
ToolTip="{Binding CurrentPath}"
Content="{Binding CurrentPath, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
SetBindings();
}
#region Properties
public static readonly DependencyProperty CurrentPathProperty =
DependencyProperty.Register("CurrentPath", typeof(String), typeof(MainWindow), new PropertyMetadata(String.Empty, OnCurrentPathChanged));
public string CurrentPath
{
get { return (String)GetValue(CurrentPathProperty); }
set { SetValue(CurrentPathProperty, value); }
}
#endregion
#region Bindings
private void SetBindings()
{
// Label CurrentPath binding
Binding _currentPath = new Binding("CurrentPath");
_currentPath.Source = CurrentPath;
this._lblCurrentPath.SetBinding(Label.ContentProperty, _currentPath);
}
#endregion
#region Methods
private void Refresh()
{
MessageBox.Show("Refresh!");
}
private string Search()
{
WinForms.FolderBrowserDialog dialog = new WinForms.FolderBrowserDialog();
WinForms.DialogResult _dResult = dialog.ShowDialog();
switch(_dResult)
{
case WinForms.DialogResult.OK:
CurrentPath = dialog.SelectedPath;
break;
default:
break;
}
return CurrentPath;
}
#endregion
#region Events
private static void OnCurrentPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MainWindow instance = d as MainWindow;
instance.Refresh();
}
public void OpenSearchEclipsePath(object sender, RoutedEventArgs e)
{
CurrentPath = Search();
}
public void RefreshEclipsePath(object sender, RoutedEventArgs e)
{
Refresh();
}
Any idea?
.If this is a bad practice and I should use MVVM comments are welcome, of ourse.
.Also... Related to Command property. In this case where I don't want to use a MVVM approach, is it better to register events? I found the use of custom command bindings a little bit tedious.
First, you can totally use bindings without MVVM. I wouldn't reccommend it, as the code is a lot cleaner when you use MVVM, but it can be done. All you need to do is put this line in your constructor:
this.DataContext = this;
Now your view is also your view model! Like I said, not a good idea.
Now, the code you have has a DependencyProperty in your MainWindow class. Don't do that. It serves absolutely no purpose. DPs are there so parent controls can give a binding to them. MainWindow has no parent; so a DP is useless.
All you need to do is set up a regular property:
public string CurrentPath
{
get { return currentPath; }
set
{
currentPath = value;
NotifyPropertyChanged();
}
}
And then have MainWindow implement INotifyPropertyChanged (did I mention that it makes more sense to use a simple view model?).
To answer your Command question. Yes, if you are opposed to using commands, just register for the events. However, Command is a really nice way to get user clicks into the view model without breaking MVVM. The syntax isn't that bad. If you are going the "View as a View Model" approach anyways though, Command doesn't buy you much.