I have an issue with binding the property Message to the view.
Callback returns a result from a WCF Service. I'm trying to assign this result to the property Message. My text box is never updated with new value - it always displays TEST.
public class CallbackHandler : IDataExchangeCallback, INotifyPropertyChanged
{
public CallbackHandler()
{
this.Message = "TEST";
}
public void Result(string result)
{
Message = result;
}
private string _message;
public string Message
{
get { return _message; }
set
{
_message = value;
OnPropertyChanged("Message");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
<Window x:Class="guiClient.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tb="http://www.hardcodet.net/taskbar"
xmlns:local="clr-namespace:guiClient"
Title="DataExchangeClient" Height="76" Width="297" WindowStyle="SingleBorderWindow" MinHeight="50" MinWidth="50" MaxWidth="300">
<Window.DataContext>
<local:CallbackHandler/>
</Window.DataContext>
<Grid>
<TextBox HorizontalAlignment="Left" Height="45" TextWrapping="Wrap" VerticalAlignment="Top" Width="289" Text="{Binding Path=Message}"/>
</Grid>
</Window>
HERE IS INTERFACE:
------From UserBuzzer
Callback is defined like this :
IDataExchangeCallback Callback
{
get
{
return OperationContext.Current.GetCallbackChannel<IDataExchangeCallback>();
}
}
And interface:
// The callback interface is used to send messages from service back to client.
// The Result operation will return the current result after each operation.
public interface IDataExchangeCallback
{
[OperationContract(IsOneWay = true)]
void Result(string result);
}
The reason could be that you're not raising PropertyChanged on the UI thread, since you're calling it from a callback. Try using Dispatcher to make sure the event is raised on UI thread:
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
Application.Current.Dispatcher.Invoke(() =>
handler(this, new PropertyChangedEventArgs(propertyName)));
}
}
I found solution. It's very bad, but atm i don't know how to raise event on UI thread.
namespace guiClient
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, IDataExchangeCallback
{
public MainWindow()
{
InitializeComponent();
Register();
}
public void Result(string result)
{
//this will not cause the application to hang
Application.Current.Dispatcher.BeginInvoke(new Action(
() => textBox.Text = result));
}
public void Register()
{
InstanceContext instanceContext = new InstanceContext(this);
DataExchangeClient client = new DataExchangeClient(instanceContext);
client.RegisterClient(Guid.NewGuid().ToString());
}
}
}
As Damir Arh mention i used dispather. In this case i named control and passed result to Text property.
Notice also MainWindow now inherits from IDataExchangeCallback.
This is also tricky: InstanceContext instanceContext = new InstanceContext(this);
If anyone know how to implement this in MVVM patern give me a call.
Related
I'm having troubles with my UI not updating when my properties change, even though I have INotifyPropertyChange applied. When i run the code the first time, it shows up correct and the UI is updated. While debbuging I can see the new values being set to the strings of the viewmodel and that the OnPropertChange event is fired, it just don't happen anything in the UI. The code below will be in order of events. As extra information, I use the same code to update the viewmodel both in the first and second time.
public partial class Transaktioner : Window
{
ViewModelCommon.ViewModel view = new ViewModelCommon.ViewModel();
private static List<ViewModelCommon.Items2> getAccountingRowsListEdited = new List<ViewModelCommon.Items2>();
{
DataContext = view;
InitializeComponent();
}
private async Task GetAccountinTransactionsAsync()
{
await Task.Run(() =>
{
getAccountingRowsList = client.GetAccountingTransactions(ftglist[index], 0, ref status).ToList();
foreach (var v in getAccountingRowsList)
{
getAccountingRowsListEdited.Add(new ViewModelCommon.Items2
{
itemName2 = v.ver.ToString(),
value2 = v.text,
vertyp = v.vtyp,
s2 = v.kto.ToString(),
s3 = v.trdat.ToString()
});
}
Task.Run(async () =>
{
await SearchAndDisplayResult();
});
});
}
private async Task SearchAndDisplayResult(int exclusion = 0)
{
await Task.Run(() =>
{
var verfikationer = getAccountingRowsListEdited.Where(u => u.vertyp != exclusion).Count(u => u.s2.ToString().Equals("0"));
view.VerifikationerTotal = verfikationer.ToString();
});
}
The ViewModel:
class ViewModelCommon
{
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName]string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, newValue))
{
field = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
return true;
}
return false;
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected void Test(string sb)
{
Transaktioner tr = new Transaktioner("");
tr.ExcludeStringChanged(sb);
}
}
public class ViewModel : ViewModelBase
{
private string _verifikationerTotal;
public string VerifikationerTotal
{
get { return _verifikationerTotal; }
set
{
if (value != _verifikationerTotal)
{
_verifikationerTotal = value;
OnPropertyChanged("VerifikationerTotal");
}
}
}
private string _ExcludeString;
public string ExcludeString
{
get { return _ExcludeString; }
set
{
if (value != _ExcludeString)
{
_ExcludeString = value;
OnPropertyChanged("ExcludeString");
Test(ExcludeString);
}
}
}
}
The WPF:
<TextBox x:Name="TextBoxVerifikationerTotal" Text="{Binding VerifikationerTotal}" IsEnabled="False" HorizontalAlignment="Left" Height="23" Margin="583,182,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="99"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="837,10,0,0" TextWrapping="Wrap" Text="{Binding Path=ExcludeString, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" VerticalAlignment="Top" Width="286"/>
The code above works as expected.
In the UI there is an option to introduce the optional attribute to exclude values. Those are bound to the "ExludeString" this also works and fires the event passing it again to the SearchAndDisplayResult(int exclusion = 0) with the replaces value of the int being passed. While debugging I can see that the event can successfully find a new value and passing it to the ViewModel, but it doesn't update the UI.
Are there any thoughts on why the UI is not updated? Thank you in advance!
The code has been shortend to show the vitals
Answer for this case was the
ViewModelCommon.ViewModel view = new ViewModelCommon.ViewModel();
not being set to a private static while working with Tasks.
I have a button like this:
<Button Content="Gönder" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="932,23,0,0" Height="25" Command="{Binding Path=SetTeamList}" CommandParameter="{Binding ElementName=UrlBox, Path=Text}"/>
And at the VM, i have a method
public void SetTeamList(string Url)
{
//Some things here
}
The solution is WinForms app, so i set DataContext like this:
var view = new dTeamMapperForm();
view.DataContext = new TeamMappingVM();
elementHost1.Child = view;
Nothing happens when i click the button, no error or something. I put break point to SetTeamList method and it's not executing on button click.
Edit: I have changed the whole VM, now it looks like:
class TeamMappingVM : INotifyPropertyChanged
{
public ObservableCollection<Team> TeamList { get; set; }
public ICommand SetTeamsCommand { get; internal set; }
private string _url;
public string Url
{
get { return _url; }
set
{
_url = value;
NotifyPropertyChanged("Url");
}
}
public void SetTeamList()
{
var mapper = new TeamMapper();
TeamList = new ObservableCollection<Team>(mapper.MapTeams(Url));
}
public bool CanParseTeams()
{
return !string.IsNullOrEmpty(Url);
}
public TeamMappingVM()
{
SetTeamsCommand = new RelayCommand(SetTeamList, CanParseTeams);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
The Command-Property of a Button expects you to Bind to an Property of type ICommand.
In your Case you tried to Bind to a method, which does not work.
Since you edited you post i will just post this as the answer:
XAML:
<Button Content="Gönder" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="932,23,0,0" Height="25" Command="{Binding Path=SetTeamsCommand }" CommandParameter="{Binding ElementName=UrlBox, Path=Text}"/>
class TeamMappingVM : INotifyPropertyChanged
{
public ObservableCollection<Team> TeamList { get; set; }
public ICommand SetTeamsCommand { get; internal set; }
private string _url;
public string Url
{
get { return _url; }
set
{
_url = value;
NotifyPropertyChanged("Url");
}
}
public void SetTeamList()
{
var mapper = new TeamMapper();
TeamList = new ObservableCollection<Team>(mapper.MapTeams(Url));
}
public bool CanParseTeams()
{
return !string.IsNullOrEmpty(Url);
}
public TeamMappingVM()
{
SetTeamsCommand = new RelayCommand(SetTeamList, CanParseTeams);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
As a minor side note. Which was not asked. Since you are probably Using Databinding for your URL Textbox you don't need to pass it into the method via CommandParameter, Since the URL Property of your ViewMOdel represents this textbox. You want to try to seperate the view from the logic. This is a very small issue and might not have any effect, but it sort of is a bad habit to fall into.
As Xeun pointed out, a Command is not a method but an object implementing the ICommand interface. A Command implementation look like this:
class MyCommand: ICommand
{
public bool CanExecute(object parameter)
{
return true; // if your command is "enabled" otherwhise return false
}
public void Execute(object parameter)
{
// do something usefull
}
}
In this sample you should add an instance of MyCommand to your ViewModel an
bind to it.
Please notice usually you dont code commands this way.
A command usually interact with your ViewModel (ie it invokes Model methods) and inside MyCommand you have not references to the ViewModel hosting it.
(You could create a Command which hold a reference to its ViewModel, but...) Usually inside a ViewModel you use a Relay command or a Delegate command (which are basically the same thing).
I'm new to WPF, so there's probably something basic I'm missing here. I have an application that looks like this:
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test Application" Height="647" Width="723" Background="#88B0FF">
<DockPanel Name="MainDock">
<Button DockPanel.Dock="Top" Margin="5,0,5,0" x:Name="PingButton" Click="PingButton_OnClick">Ping</Button>
<TextBox Text="{Binding Path=Output}" />
</DockPanel>
</Window>
The code-behind is like this:
public partial class MainWindow : Window
{
private Model _applicationModel = new Model();
public Model ApplicationModel {
get { return _applicationModel; }
set { _applicationModel = value; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = ApplicationModel;
ApplicationModel.Output = "Not clicked";
}
private void PingButton_OnClick(object sender, RoutedEventArgs e)
{
ApplicationModel.Output = "Clicked";
}
}
I have a small class called Model that implements INotifyPropertyChanged.
public class Model : INotifyPropertyChanged
{
public string Output { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I run this application, and the text box displays the text "Not clicked". When I click the button, I would expect that the text would change. It does not. The "ApplicationModel" object is updated, and this is reflected in the DataContext; I have a breakpoint in the OnPropertyChanged() method, however, and it appears that it's never being called.
What am I doing wrong?
OnPropertyChanged() isn't being called because you're not calling it.
There is no special magic that wires up calls to OnPropertyChanged by itself, so you need to do it yourself.
Specifically, you should modify your Output property to call it when it changes (and it wouldn't hurt to do the same for your ApplicationModel property:
private string output;
public string Output
{
get { return output; }
set
{
if (output != value)
{
output = value;
OnPropertyChanged("Output");
}
}
}
If you're targeting .NET 4.5 you can utilize the CallerMemberName attribute to reduce boilerplate code; This article explains how to do so. Then you'll have something like this:
private string output;
public string Output
{
get { return output; }
set { SetProperty(ref output, value); }
}
If you're using .NET 4.0 or below, you can use expression trees, as described in this answer.
I have an MVVM app, where my ViewModel PingerViewModel processes incoming WCF Ping() messages. Processing such a message happens on a thread of Scheduler.Default's thread pool.
Semantically, incoming WCF messages change a bound property CanPing and raise the PropertyChanged event for said property.
But my UI is not updating until it receives some UI event, e.g. focusing/clicking the window, etc.
How do I make it update as soon as the event is fired?
I have tried raising the PropertyChanged event...
on the Application's Dispatcher,
using a SynchronizationContext
without any luck.
I also verified that the bound property is indeed set to the proper value, and that there is indeed a listener consuming my PropertyChanged event.
Here's some code (full code on github):
part of my view's MainWindow.xaml:
It might be worth noting that the bound Command does not actually play a role in producing the incoming WCF message.
<Button Content="Ping" Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="PingBtn" VerticalAlignment="Top" Width="75" AutomationProperties.AutomationId="Ping"
IsEnabled="{Binding CanPing}"
Command="{Binding PingCommand}" />
part of my views MainWindow.xaml.cs
public MainWindow()
{
DataContext = new PingerViewModel();
InitializeComponent();
}
part of my ViewModel
public class PingerViewModel : INotifyPropertyChanged
public PingerViewModel()
{
Pinger = new Pinger(true);
PingCommand = new PingerPingCommand(this);
//...
}
public bool CanPing
{
get
{
if (Pinger == null) return false;
return Pinger.CanPing;
}
}
public void Ping()
{
_pingClient.Channel.Ping();
Pinger.CanPing = false;
OnPropertyChanged("CanPing");
}
protected virtual void OnPong(PongEventArgs e)
{
Pinger.CanPing = true;
OnPropertyChanged("CanPing");
}
public Pinger Pinger { get; private set; }
public ICommand PingCommand { get; private set; }
//...
}
I think you need to remove IsEnabled="{Binding CanPing}" from your button.
Binding to the command is enough as the ICommand object contains CanExecute and the CanExecuteChanged event handler.
I would create a CanExecute boolean inside your Command class, and implement INotifyPropertyChanged also on this class. Something like this:
public class PingCommand : ICommand, INotifyPropertyChanged
{
private bool _canExecute;
public bool CanExecute1
{
get { return _canExecute; }
set
{
if (value.Equals(_canExecute)) return;
_canExecute = value;
CanExecuteChanged.Invoke(null, null);
OnPropertyChanged("CanExecute1");
}
}
public void Execute(object parameter)
{
//whatever
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Then, on the Ping/Pong methods inside your ViewModel, update this property inside your Command:
public void Ping()
{
_pingClient.Channel.Ping();
Pinger.CanPing = false;
PingCommand.CanExecute1 = false;
OnPropertyChanged("CanPing");
}
protected virtual void OnPong(PongEventArgs e)
{
Pinger.CanPing = true;
PingCommand.CanExecute1 = true;
OnPropertyChanged("CanPing");
}
if your CanPing Property and the CanExecute methode for your PingCommand both return TRUE it should work.
sometimes the Delegate/RelayCommand implementation give the possibility to call RaiseCanExecuteChanged() - try this if the statement above is true for both and its not working
btw this is called within the RaiseCanExecuteChanged()
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
You can use RaiseCanExecuteChanged() method of the said property to update.
Eg:
this.PingCommand.RaiseCanExecuteChanged();
Try this, I hope it will solve your problem.
I am just getting started with MVVM so apologies if I've done something really stupid. I tried writing a very simple test to see if I could remember everything, and for the life of me I can't see why its not working.
In my view I have a textBox where its text property is bound to a value in the ViewModel. Then when pressing a button the value should be altered and the textBox update.
I can see the value does alter (I have added a MessageBox.Show() line in the buttom press command) however the textBox does not update.
I assume that this means I have not properly implemented the INotifyPropertyChanged event properly but am unable to see my mistake.
Could anyone point me in the right direction?
Here is the code:
View
<Window x:Class="Mvvm.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<TextBox Height="40" Width="200" Text="{Binding helloWorld.Message, UpdateSourceTrigger=PropertyChanged}"/>
<Button Command="{Binding UpdateTimeCommand}">Update</Button>
</StackPanel>
</Window>
Behind View
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel.MainWindowViewModel();
}
}
ViewModel
namespace Mvvm.ViewModel
{
internal class MainWindowViewModel
{
private HelloWorld _helloWorld;
/// <summary>
/// Creates a new instance of the ViewModel Class
/// </summary>
public MainWindowViewModel()
{
_helloWorld = new HelloWorld("The time is " + DateTime.Now.ToString("HH:mm:ss"));
UpdateTimeCommand = new Commands.UpdateTimeCommand(this);
}
/// <summary>
/// Gets the HellowWorld instance
/// </summary>
public HelloWorld helloWorld
{
get
{
return _helloWorld;
}
set
{
_helloWorld = value;
}
}
/// <summary>
/// Updates the time shown in the helloWorld
/// </summary>
public void UpdateTime()
{
helloWorld = new HelloWorld("The time is " + DateTime.Now.ToString("HH:mm:ss"));
}
public ICommand UpdateTimeCommand
{
get;
private set;
}
}
Model
namespace Mvvm.Model
{
class HelloWorld : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public HelloWorld(string helloWorldMessage)
{
Message = "Hello World! " + helloWorldMessage;
}
private string _Message;
public string Message
{
get
{
return _Message;
}
set
{
_Message = value;
OnPropertyChanged("Message");
}
}
private void OnPropertyChanged(string p)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(p));
}
}
}
}
Commands
namespace Mvvm.Commands
{
internal class UpdateTimeCommand : ICommand
{
private ViewModel.MainWindowViewModel _viewModel;
public UpdateTimeCommand(ViewModel.MainWindowViewModel viewModel)
{
_viewModel = viewModel;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_viewModel.UpdateTime();
}
}
}
Sorry for such a long post and it being a spot my mistake post but I've looked at it for so long and I don't know what I'm doing wrong
Thanks!
The Problem that you have is that you are changing the wrong Property. Instead of changing the HelloWorld.Message Property, you are changing MainWindowViewModel.HelloWorld property. Your code will work OK if you change this line:
public void UpdateTime()
{
helloWorld = new HelloWorld("The time is " + DateTime.Now.ToString("HH:mm:ss"));
}
For this one
public void UpdateTime()
{
helloWorld.Message = "The time is " + DateTime.Now.ToString("HH:mm:ss");
}
If you want to keep your original code, then you need to implement INotifyPropertyChanged for your ViewModel, and rise the event when you change helloWorld object.
Hope this helps
I think you need to implement PropertyChanged notification on your ViewModel. You are creating a new HelloWorld in the UpdateTime method, but the UI doesn't know it.
Edit
I have a ViewModel base class which I derive all of my ViewModels from. It implements INotifyPropertyChanged, and has references to my relay command classes, and some other common stuff. I recommend always having INotifyPropertyChanged implemented on the ViewModel. The ViewModel is there to expose data to the UI, and it cant do that for data that changes without that interface.
i think your ViewModel needs to implement INotifyPropertyChanged too,
or you can set the DataContext before you call InitializeComponents(), if you do that you should change your code to NOT create a new instance every update like Agustin Meriles said.
i think you mistake Model and VM: Model is MainWindowViewModel and VM is HelloWorld
In your VM (class HelloWorld ) you need use your model
So, your classes will look like:
using System.ComponentModel;
namespace WpfApplication1
{
public sealed class TextVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private TextInfo _info;
public TextVM()
{
_info = new TextInfo();
}
public string MyText
{
get { return _info.MyText; }
set
{
_info.MyText = value;
OnPropertyChanged("MyText");
}
}
private void OnPropertyChanged(string p)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(p));
}
}
}
}
using System;
namespace WpfApplication1
{
public sealed class TextInfo
{
public TextInfo()
{
MyText = String.Empty;
}
public string MyText { get; set; }
}
}
inset inside your ICommands