@glasswizzard I hope you will appreciate that I've taken the time to finish this for you, as I've probably save Skydiver a lot of time by not dumping this code on his lap unfinished, as I also know he has better things to do. And I've taken the time out of my personal work schedule, when I should have been completing work for my contract employer. However, if you need further help with my example, you will need to wait for one of the mods to assist you, as I will be away for the coming weeks. I'm not quite sure for how long. So here is what you asked for :
And here is the code I used. Lets start with the Main_ViewModel :
public class Main_ViewModel
{
public ObservableValue New_Value { get; }
public Main_ViewModel()
{
New_Value = new ObservableValue();
}
}
The main view model constructor returns a new class of ObservableValue of the value stored therein :
private string TextBlock_Value;
of this class. This is done through its GetSetter property :
public string TextBlock_Text
{
get
{
if (string.IsNullOrEmpty(TextBlock_Value))
return "Nothing Detected";
return TextBlock_Value;
}
set
{
TextBlock_Value = value;
OnPropertyChanged();
}
}
in the ObservableValue class :
public class ObservableValue : INotifyPropertyChanged
{
private string TextBlock_Value;
public string TextBlock_Text
{
get
{
if (string.IsNullOrEmpty(TextBlock_Value))
return "Nothing Detected";
return TextBlock_Value;
}
set
{
TextBlock_Value = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string new_Value = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(new_Value));
}
}
Notice that we also have a public event
PropertyChanged
-- (Pretty much its a delegate to raise an event which helps with passing the method of the calling code when a component value of the INotifyPropertyChanged has been changed, thus raises the event therein) fires of the below method :
protected void OnPropertyChanged([CallerMemberName] string new_Value = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(new_Value));
}
Quickly moving to the Main Xaml.cs file. I have declared a
field :
private Main_ViewModel main_ViewModel = new Main_ViewModel();
And in the constructor I have :
public Window1()
{
InitializeComponent();
DataContext = main_ViewModel;
Task Begin = new Task(() => Set_Default_Values());
Begin.Start();
}
Notice that I've added my
DataContext
to equal that of my
main_ViewModel
which is essentially our
main_ViewModel
=>
MainViewModel
. Next, (I don't know why), but I update the default value of the textBlock Control by way of a new task. I just wasn't following the guidelines of the
MVVM here, but it doesn't really matter as this is another way of updating your UI appropriately. Our
Set_Default_Values()
method is what first sets the textBlock
Text
property :
private void Set_Default_Values()
{
myTextBlock.Dispatcher.Invoke(new Delegate_Callback_To(UpdateUI_With_NewItem), new string[] { "Nothing Detected" });
}
Because the Task is running on a non UI thread, you will need to call
Dispatcher.Invoke()
to first invoke the control by passing our :
UpdateUI_With_NewItem
method to our Delegate. :
public delegate void Delegate_Callback_To(string str_Value);
. Note that this Delegate takes a string value; for
type safety, I've used a string and not a base class object, but you can use whatever object you want for whatever object you are passing in. As you can see, I am passing in the value of "Nothing Detected" to our delegate which will pass along this string from the Delegate
Delegate_Callback_To
to the
UpdateUI_With_NewItem
method in the following code :
myTextBlock.Dispatcher.Invoke(new Delegate_Callback_To(UpdateUI_With_NewItem), new string[] { "Nothing Detected" });
The
UpdateUI_With_NewItem
is the
method that is used in this instance to update the value of our textBlock :
public void UpdateUI_With_NewItem(string item_Value)
{
myTextBlock.Text = item_Value;
}
You should note, that at this point, this has nothing to do with binding, and I am just showing you an alternative safe way to update your
UI without the
MVVM pattern which I would prefer you to follow. For the sake of pulling it out, and breaking something, I just left it in there, as its also worth knowing how else you can update your
UI. No matter what you are doing with your UI, never change values of your UI, or allow
worker methods to run on your
UI, and always follow either of these principles in this example when working with your
UI.
Getting back on track with the MVVM and bindings here. Sorry for the detour but I'd prefer you know and prefer to give it to you as I've wrote it instead of pulling something out and providing you with a broken example, and besides it's also educational for you. And before I continue, I would also like you to look up everything I've highlighted with inline code and italic with the exception of custom code.
Next I added these
MouseMove events :
private void T0_MouseMove(object sender, MouseEventArgs e)
{
main_ViewModel.New_Value.TextBlock_Text = textBox0.Text;
}
private void T1_MouseMove(object sender, MouseEventArgs e)
{
main_ViewModel.New_Value.TextBlock_Text = textBox1.Text;
}
private void T2_MouseMove(object sender, MouseEventArgs e)
{
main_ViewModel.New_Value.TextBlock_Text = textBox2.Text;
}
These events use the
Main_ViewModel
which depends on the
ObservableValue
to update the text property of the textBlock. Right so fare I've covered all the classes and explained how it works. You should also note that you will need the following using directives :
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
Here is my Xaml :
<Window xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
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:WPFTestApp" mc:Ignorable="d"
x:Class="WPFTestApp.Window1" Title="Window1" x:Name="MyWindow1">
<Window.DataContext>
<local:Main_ViewModel/>
</Window.DataContext>
<ScrollViewer x:Name="ScrollView_Main" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Visible">
<Grid x:Name="MyGrid" Width="Auto" ShowGridLines="false">
<Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="365,455,0,0" VerticalAlignment="Top" Width="75"/>
<TextBlock x:Name="myTextBlock" HorizontalAlignment="Left" Margin="215,409,0,0" TextWrapping="Wrap"
Text="{Binding New_Value.TextBlock_Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top"/>
<TextBox x:Name="textBox0" HorizontalAlignment="Left" Height="23" Margin="35,310,0,0" TextWrapping="Wrap" Text="Hello" VerticalAlignment="Top" Width="120" MouseMove="T0_MouseMove"/>
<TextBox x:Name="textBox1" HorizontalAlignment="Left" Height="23" Margin="180,310,0,0" TextWrapping="Wrap" Text="My" VerticalAlignment="Top" Width="120" MouseMove="T1_MouseMove"/>
<TextBox x:Name="textBox2" HorizontalAlignment="Left" Height="23" Margin="320,310,-21,0" TextWrapping="Wrap" Text="Wizzard" VerticalAlignment="Top" Width="120" MouseMove="T2_MouseMove"/>
</Grid>
</ScrollViewer>
</Window>
Sorry I didn't do anything fancy here like Skydiver has done in previous examples with custom templating etc. However, the important parts to note here is what I have added in the way for the bindings to actually work. And they are :
- DataContext which we also set in our Codebehind file.
- Binding : Which point to the sub properties/methods of whatever your Model consists of. Ie :
Text="{Binding New_Value.TextBlock_Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Note on the last bullet that the binding starts with the property
New_Value :
public ObservableValue New_Value { get; }
of our Main
Main_ViewModel. I've also set the mode as TwoWay, and importantly added :
UpdateSourceTrigger=PropertyChanged
to this binding to work. Please remember, I will be away for some time as I am not a hobbyist programmer and one who is contracted out, so it may be a while before I come back. Any questions you have will have to be answered by one of the other contributors, that includes any corrections you may need help altering.
By the time you finish following the instructions, you should have something that looks like the full .cs file as follows :
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace WPFTestApp
{
public partial class Window1 : Window
{
private Main_ViewModel main_ViewModel = new Main_ViewModel();
public Window1()
{
InitializeComponent();
DataContext = main_ViewModel;
Task Begin = new Task(() => Set_Default_Values());
Begin.Start();
}
public delegate void Delegate_Callback_To(string str_Value);
public void UpdateUI_With_NewItem(string item_Value)
{
myTextBlock.Text = item_Value;
}
private void Set_Default_Values()
{
myTextBlock.Dispatcher.Invoke(new Delegate_Callback_To(UpdateUI_With_NewItem), new string[] { "Nothing Detected" });
}
private void T0_MouseMove(object sender, MouseEventArgs e)
{
main_ViewModel.New_Value.TextBlock_Text = textBox0.Text;
}
private void T1_MouseMove(object sender, MouseEventArgs e)
{
main_ViewModel.New_Value.TextBlock_Text = textBox1.Text;
}
private void T2_MouseMove(object sender, MouseEventArgs e)
{
main_ViewModel.New_Value.TextBlock_Text = textBox2.Text;
}
}
public class ObservableValue : INotifyPropertyChanged
{
private string TextBlock_Value;
public string TextBlock_Text
{
get
{
if (string.IsNullOrEmpty(TextBlock_Value))
return "Nothing Detected";
return TextBlock_Value;
}
set
{
TextBlock_Value = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string new_Value = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(new_Value));
}
}
public class Main_ViewModel
{
public ObservableValue New_Value { get; }
public Main_ViewModel()
{
New_Value = new ObservableValue();
}
}
}
As an aside I'd advise breaking out of nesting classes in one cs file as I have done, and create a hierarchy folder structure for your models, business logic, etc - Learn to read it by debugging it, and take note of the quote in my signature.
Speak to you all soon.