c# wpf data binding not happening. - c#

I am a bit new on c# WPF.
I have been following MVVM pattern and everything is set, my code seem to work fine but Issue I am facing is when I bind the data on xaml file, the data I am receiving from get set property but binding seems to have gone as no data is displayed on my text box. check my code.
/**********************xaml code***********************************\
<UserControl x:Class="ILS.debugger.debuggermsg"
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:serial="clr-namespace:ILS.VM.Serial_Monitor;assembly=ILS.VM"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox Text="{Binding Debugger_Recoreded}" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Background="#FFEBD3D3">
</TextBox>
</Grid>
</UserControl>
/***********************viewmodel code******************\
namespace ILS.VM.Serial_Monitor
{
public class serial : NotifyPropertyChanged
{
private string debuger_rec;
public string Debugger_Recoreded
{
get { return debuger_rec; }
set
{
if (this.debuger_rec == value)
return;
this.debuger_rec = value;
i--;
if (i == 0)
{
this.debuger_rec = String.Empty;
i = 1000;
}
this.InvokePropertyChanged("Debugger_Recoreded");
}
}
/***********************model******************\
namespace ILS
public void OnDebugger(String Receved_Data) //debug message monitor code
{
try
{
this.serialData.Debugger_Recoreded += " " + DateTime.Now + " " + Receved_Data + Environment.NewLine;
this.serialData.Debugger_Recoreded += Environment.NewLine;
}
catch (Exception e)
{
}
}

public class serial : INotifyPropertyChanged
{
private string debuger_rec;
public string Debugger_Recoreded
{
get { return debuger_rec; }
set
{
if (this.debuger_rec == value)
return;
this.debuger_rec = value;
i--;
if (i == 0)
{
this.debuger_rec = String.Empty;
i = 1000;
}
OnPropertyChanged("Debugger_Recoreded");
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
And set DataContext too, in main windows enter this lines:
this.DataContext = serialData;
Also you can use mode way to bind.
<TextBox Text="{Binding Debugger_Recoreded,Mode="Towway"}" />

In your code-behind (i.e your debuggermsg class), you have to instantiate and assign a DataContext:
public debuggermsg()
{
InitializeComponent();
this.DataContext = new serial();
}
It is required for DataBinding, so that you will be able to interact with your ViewModel's properties.
Then, modify your ViewModel like so:
public class serial : INotifyPropertyChanged
{
private string debuger_rec;
public string Debugger_Recoreded
{
get { return debuger_rec; }
set
{
if (this.debuger_rec == value)
return;
this.debuger_rec = value;
i--;
if (i == 0)
{
this.debuger_rec = String.Empty;
i = 1000;
}
OnPropertyChanged("Debugger_Recoreded");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Implementation of OnPropertyChanged method is required to notify your view of a modification of your ViewModel's property.
Everything should be fine then.

When implementing INotifyPropertyChanged it's best to use [CallerMemberName] attribute, it's in the System.Runtime.CompilerServices, as you don't have to hardcode a string name of the calling property:
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Now you can write your property like this:
private string debuggerRecorded;
public string DebuggerRecorded
{
get
{
return debuggerRecorded;
}
set
{
if (debuggerRecorded != value)
{
this.debuggerRecorded = value;
i--;
if (i == 0)
{
this.debuggerRecorded = String.Empty;
i = 1000;
}
OnPropertyChanged(); // No argument needed
}
}
}
By doing this you don't have to worry about spelling and you can freely change the names of your properties in the future, you don't have to remember to change it also in the OnPropertyChanged.
Assuming everything else works fine with your code you just need to set DataContext, which is usually done in MainWindow. For example, like this:
public partial class MainWindow : Window
{
private Serial viewModel;
public MainWindow()
{
InitializeComponent();
viewModel = new Serial();
this.DataContext = viewModel;
}
}
Also, you may want to write your text box with another property:
TextBox Text="{Binding DebuggerRecorded, UpdateSourceTrigger=PropertyChanged}" ...
If you omit this last part, the Text will get updated only when the TextBox loses focus.

Related

Binding ObservableCollection<class> fields to ListBox DataTemplate

I am a student that just finished up a summer internship, and I brought home a project to work on briefly before school starts up. This project has a stopwatch in it, and I would rather use an ObservableCollection bound to my ListBox for my split times, rather that using the listbox.Items.Add(). When I add to the ObservableCollection, the ListBox UI does not update. Could anyone point me in the right direction on what I missed or what I did wrong?
I have my TimeSplits class:
public class TimeSplits : INotifyPropertyChanged
{
private int _hours;
private int _minutes;
private int _seconds;
public int hours
{
get
{
return _hours;
}
set
{
_hours = value;
NotifyPropertyChanged(hours);
}
}
public int minutes
{
get
{
return _minutes;
}
set
{
_minutes = value;
NotifyPropertyChanged(minutes);
}
}
public int seconds
{
get
{
return _seconds;
}
set
{
_seconds = value;
NotifyPropertyChanged(seconds);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(int propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(nameof(propertyName)));
}
}
public override string ToString()
{
return hours.ToString() + ":" + minutes.ToString() + ":" + seconds.ToString();
}
}
and my ObservableCollection in my Page:
public partial class StopwatchPage : Page , INotifyPropertyChanged
{
...
public ObservableCollection<TimeSplits> splits = new ObservableCollection<TimeSplits>();
...
public StopwatchPage()
{
DataContext = this;
InitializeComponent();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += new EventHandler(stopwatchTimer);
}
...
private void splitButton_Click(object sender, RoutedEventArgs e)
{
TimeSplits split = new TimeSplits();
split.hours = Hours;
split.minutes = Minutes;
split.seconds = Seconds;
splits.Add(split);
}
...
}
and my xaml:
<ListBox x:Name="newSplitListBox" HorizontalAlignment="Left" Margin="139,0,0,47" Width="185" Height="268" VerticalAlignment="Bottom" ItemsSource="{Binding splits}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding hours}"/>
<TextBlock Text="{Binding minutes}"/>
<TextBlock Text="{Binding seconds}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I am sure it is something small that I have no clue about, as I just started learning data binding this summer. Any help is greatly appreciated! Thanks in advance.
It looks like you have nameof() in the wrong place. The way your current code reads, it will always send the value of "propertyName" as the name of the property that changed, regardless of what property actually changed.
Try this:
public int hours
{
get
{
return _hours;
}
set
{
_hours = value;
NotifyPropertyChanged();
}
}
Then, in your NotifyPropertyChanged(), do this:
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
}
Edit: Added fix for the following:
Also, the ObservableCollection needs to be a property. Change this code:
public ObservableCollection<TimeSplits> splits = new ObservableCollection<TimeSplits>();
To this:
public ObservableCollection<TimeSplits> Splits { get; set; } = new ObservableCollection<TimeSplits>();
I learned a trick from Xamarin's ViewModel template that helped me immensely. Here is the code that it generates that handles an observable View Model (much like the ObservableCollection).
protected bool SetProperty<T>(ref T backingStore, T value,
Action onChanged = null,
[CallerMemberName]string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
Then, to use this, simply add this to your properties:
private string _title = string.Empty;
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}

WPF C# PropertyChanged always null

I have played around with this for a while and decided to see if someone can help, I have set in the constructor of StatusInfo the DataContext = this and didn't work. When I write a string to ScreenStatusBarText it does call the OnPropertyChanged method but every time the PropertyChanged value is null. I The status block I have at the bottom of the screen. I have a tab section above this stack panel that has many components that use bindings and work.
Screen Code
<StackPanel Margin="0,1047,0,0">
<Grid Name="StatusBarItemGrid">
<TextBlock Name="StatusBarText" Text="may the force be with you" VerticalAlignment="Center" HorizontalAlignment="Stretch" />
</Grid>
</StackPanel>
Data Model:
public partial class StatusInfo : INotifyPropertyChanged
{
private string screenStatusBarText;
public StatusInfo()
{
BindScreenStatusBarText();
screenStatusBarText = "Initialized";
}
public string ScreenStatusBarText
{
get { return screenStatusBarText; }
set
{
screenStatusBarText = value;
OnPropertyChanged("StatusBarText");
}
}
private void BindScreenStatusBarText()
{
Binding b = new Binding();
b.Source = screenStatusBarText;
b.Mode = BindingMode.OneWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
b.Path = new PropertyPath("StatusBarText");
MainWindow.mainWindow.StatusBarText.SetBinding(TextBlock.TextProperty, b);
MainWindow.mainWindow.StatusBarText.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
My main :
public partial class MainWindow : Window
{
public static StatusInfo status;
public MainWindow()
{
InitializeComponent();
SourceInitialized += MainWindow_SourceInitialized;
}
private void MainWindow_SourceInitialized(object sender, EventArgs e)
{
SetUpDisplay();
}
private void SetUpDisplay()
{
status = new StatusInfo();
}
}
Set the Binding in XAML instead of code behind:
<TextBlock Text="{Binding ScreenStatusBarText}" />
And use a view model like
public class StatusInfo : INotifyPropertyChanged
{
private string screenStatusBarText = "Initialized";
public string ScreenStatusBarText
{
get { return screenStatusBarText; }
set
{
screenStatusBarText = value;
OnPropertyChanged(nameof(ScreenStatusBarText));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(propertyName));
}
}
with an instance of the view model class assigned to the MainWindow's DataContext:
private readonly StatusInfo statusInfo = new StatusInfo();
public MainWindow()
{
InitializeComponent();
DataContext = statusInfo;
}
You may now access the view model class at any time later, e.g. in an event handler of an element of MainWindow:
statusInfo.ScreenStatusBarText = "Something";
I think your going to struggle doing your binding in code behind.
Having said that, with regards to why your PropertyChanged value is null. You've simply made a typo, as-is you're notifying subscribers that a property that doesn't exist has changed. One solution to avoid such typos is to use nameof.
public string ScreenStatusBarText
{
get { return screenStatusBarText; }
set
{
screenStatusBarText = value;
OnPropertyChanged(nameof(ScreenStatusBarText));
}
}
It occurred to me you may also have meant that your event was null. This simply means you don't have any subscribers. See Why is my "Event" always null?.
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) // I have a subscriber.
handler(this, new PropertyChangedEventArgs(propertyName));
}

How do I correctly subscribe to a PropertyChange event of another class?

I am attempting to bind a CheckBox in my View to a property in my ViewModel. I am trying to subscribe to changes of the individual ViewModel property.
I have implemented INotifyPropertyChanged on my custom class, however my handler method is never called.
I have included basic examples of the View (XAML), ViewModel, and the custom user class.
ViewModel
public class HomeViewModel: ViewModelBase
{
public HomeViewModel()
{
this.selectedUser = new usersVM();
this.selectedUser.PropertyChanged += propChangedHandler;
}
private void propChangedHandler(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "superuser") // <-- breakpoint here
{
}
}
private usersVM _selectedUser;
public usersVM selectedUser
{
get { return this._selectedUser; }
set
{
if (this._selectedUser != value)
{
this._selectedUser = value;
this.RaisePropertyChanged("selectedUser");
}
}
}
}
Custom User Class
public class usersVM : INotifyPropertyChanged
{
public usersVM()
{
this.hasChanges = false;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private int _superuser;
public int superuser
{
get
{
return this._superuser;
}
set
{
if (value != this._superuser)
{
this._superuser = value;
NotifyPropertyChanged("username");
}
}
}
XAML #
<CheckBox Margin="0,0,8,0" Content="SuperUser" IsChecked="{Binding superuser}" DataContext="{Binding selectedUser}" />
I have a breakpoint in my Hadler Method to try to verify when the event is handled, but this is never called.
How can I properly implement INotifyPropertyChanged and subscribe to these events in my ViewModel?
Detach the PropertyChanged event handler from the current selectedUser value and attach it to the new one like this:
private usersVM _selectedUser;
public usersVM selectedUser
{
get { return _selectedUser; }
set
{
if (_selectedUser != value)
{
if (_selectedUser != null)
{
_selectedUser.PropertyChanged -= propChangedHandler;
}
_selectedUser = value;
if (_selectedUser != null)
{
_selectedUser.PropertyChanged += propChangedHandler;
}
RaisePropertyChanged("selectedUser");
}
}
}

ToolTip as Timer

My tooltip should show how long my program is running. So I try to add +1 to my tooltip, but that doesn't work.
That is my xaml code:
<StatusBarItem >
<Image ToolTip="{Binding Path=ToolTipStatus}"/>
</StatusBarItem>
And thats my C# code:
private string _toolTipStatus = "0";
private string ToolTipStatus
{
get { return _toolTipStatus; }
}
private void Example()
{
_toolTipStatus = _toolTipStatus + 1;
}
First, nowhere in this code is there any reason for the UI to guess when or if your private field has changed. Second, your property is private too, so the UI can't see it either. Finally, repeatedly appending "1" to a string is going to get you a string that looks like "11111111111111111111111111111" after the timer fires a few times. If that's what you want, that's fine, but I think it might not be.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MyViewModel : ViewModelBase
{
private int _toolTipStatus = 0;
private int ToolTipStatus
{
get { return _toolTipStatus; }
protected set {
if (_toolTipStatus != value)
{
_toolTipStatus = value;
OnPropertyChanged(nameof(ToolTipStatus));
}
}
}
}
private void Example()
{
ToolTipStatus += 1;
}
You won't say if you've got a viewmodel. You won't say what class your code is in or how (or if) it gets called. All your properties are private. You won't say what the XAML looks like or even if there is any. I sense a theme of obsessive secrecy here. You need to learn when to open up and share.
And you need a viewmodel, and you need it to implement INotifyPropertyChanged.
You should refresh your xaml someway. The best way I think is inheriting the form from INotifyPropertyChanged.
Then declare the event and the raise method like this
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropChanged(string name)
{
var eh = this.PropertyChanged;
if (eh != null)
{
eh(this, new PropertyChangedEventArgs(name));
}
}
then your property ToolTipStatus should be this:
private string toolTipStatus;
public string ToolTipStatus
{
get { return toolTipStatus; }
set
{
toolTipStatus = value;
RaisePropChanged("ToolTipStatus");
}
}

Update ListView items value not working Windows Phone 8.1

When i try to update value of inserted element in ListView i didn't see changes on phone, but in debug message i see changes. Here my code.
private void chat_LayoutUpdated(object sender, object e)
{
foreach (Message item in chat.Items)
{
item.MessageTime = GetRelativeDate(item.MessageDateTime);
Debug.WriteLine(item.MessageTime); //Display changed value(Delta computed on step before) but on phone screen value didn't change;
}
}
GetRelativeDate // Long function which return delta between current time and time when message was sended.
class Message // Model of chat message
{
public string MessageText { get; set; }
public string MessageTime { get; set; } // This value i want to change in ListView.
public DateTime MessageDateTime { get; set; }
}
XAML
<TextBlock Grid.Column="0"
FontSize="22"
Text="{Binding MessageText}" />
<TextBlock
Grid.Column="1"
Grid.Row="1"
FontSize="10"
Text="{Binding MessageTime}" />
P/s Maybe i need something more specific for using as chat windows.
Anyway thanks guys i will really appreciate any answers or suggestions.
Need to Implement INofityPropertyChanged
MSDN: inotifypropertychanged Example
Sample from the MSDN article:
public class DemoCustomer : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private DemoCustomer()
{
}
private string customerNameValue = String.Empty;
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
}
}
Thanks #ChubosaurusSoftware! .
Here how i implement that interface maybe some on need example, or i make not very clear and you can suggest better solution.
At first add to using System.ComponentModel;
Then change model like that.
class Message : INotifyPropertyChanged
{
public string MessageText { get; set; }
private string messageTime = String.Empty;
public string MessageTime
{
get { return this.messageTime; }
set
{
if (value != this.messageTime)
{
this.messageTime = value;
NotifyPropertyChanged("MessageTime");
}
}
}
public DateTime MessageDateTime { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I tried this and it worked for me
List<Model.BeneficiaryItem> oldListItem = listItem;
List<Model.DataType> newListItem = new List<Model.DataType>();
ListView.ItemsSource = newListItem;
//update the data
for (int i = 0; i < oldListItem.Count; i++)
{
Model.DataTyep benItem = new Model.DataTyep ();
// update the individual object benItem
newListItem.Add(benItem);
}
ListViewBenList.ItemsSource = newListItem;

Categories

Resources