Observable collection not reacting to it's collecion - c#

My ListBox doesn't react to my ObservableCollection.
This is the ListBox I am talking about.
<ListBox x:Name="CreateFieldsList"
HorizontalAlignment="Left"
Height="218"
VerticalAlignment="Top"
Width="244"
Margin="0,86,0,0"
BorderBrush="#FFB9B9B9">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="4"
Width="215"
Height="32.96"
Background="White">
<TextBlock Text="{Binding Name}"
FontWeight="Normal"
FontSize="18.667"
Padding="8,3,0,0" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In my MainWindow, I prepare the data binding like this
private ObservableCollection<FieldListItem> _fieldItems;
public MainWindow()
{
InitializeComponent();
_fieldItems = new ObservableCollection<FieldListItem>();
CreateFieldsList.ItemSource = _fieldItems;
}
A FieldListItem is following
public class FieldListItem : ViewItem
{
private Field _field;
public string Name
{
get { return _field.Name; }
}
public string Value
{
get { return _field.Value; }
}
public FieldListItem(Field f)
{
_field = f;
}
}
and finally the ViewItem
public class ViewItem : INotifyPropertyChanged
{
private event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string caller = "")
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
//The interface forces me to implement this. Why?
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add { }
remove { }
}
}
I don't know why this isn't working. Could you please help?

The INotifyPropertyChanged interface needs you to implement an event. Your event implementation does not work because registrations and deregistrations are ignored because the add and remove blocks are empty.
Implement the event without add and remove:
public class ViewItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string caller = "")
{
var copy = PropertyChanged;
if (copy != null)
copy(this, new PropertyChangedEventArgs(caller));
}
}

Related

creating dynamic controls with MVVM

I am trying to create a dynamic control with using mvvm for the first time. I want to generate buttons dynamically and have the content display inside of the buttons. I am sure I am missing something really easy here, but I have no idea what it could be. When I run the code, nothing appears on the interface, though I can see AvailableMonitorOC populate in the constructor...
Here is my ViewModel where I manually add buttons to an observable collection for simplicity sake of this example:
public class CreateAndDisplayViewModel {
public ObservableCollection<AvailableMonitorBo> AvailableMonitorOC = new ObservableCollection<AvailableMonitorBo>();
public CreateAndDisplayViewModel() {
availableMonitorBo = new AvailableMonitorBo();
availableMonitorBo.AvailableMonitorLabel = "Label 1";
AvailableMonitorOC.Add(availableMonitorBo);
availableMonitorBo.AvailableMonitorLabel = "Label 2";
AvailableMonitorOC.Add(availableMonitorBo);
}
private AvailableMonitorBo availableMonitorBo;
public AvailableMonitorBo AvailableMonitorBo {
get { return availableMonitorBo; }
set {
availableMonitorBo = value;
}
}
}
Here is my model:
public class AvailableMonitorBo : INotifyPropertyChanged {
private string availableMonitorLabel { get; set; }
public string AvailableMonitorLabel {
get { return availableMonitorLabel; }
set {
availableMonitorLabel = value;
OnPropertyChanged("AvailableMonitorLabel");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And here is the xaml:
<ListView Grid.Row="2" Grid.Column="1"
ItemsSource="{Binding AvailableMonitorOC, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding AvailableMonitorLabel}"
Width="100"
Height="25"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The main reason for you lack of display is that AvailableMonitorOC needs to be a property of CreateAndDisplayViewModel, not a field as it is currently.
You're also only creating one AvailableMonitorBo instance and changing its caption each time.

Binding a string variable to a label

I am trying to move from WinForms to WPF, and am stuck on binding.
I have a label:
<Label Name="labelState" Content="{Binding state}" HorizontalAlignment="Right" Margin="10,10,10,10" FontSize="12" />
In the cs of the same userControl (named FormInput), I have :
public string state { get; set; }
public FormInput()
{
state = "ok";
InitializeComponent();
}
Why doesn't this work?
Thank you.
When you are binding something in WPF you need to use INotifyPropertyChanged
Implement a class follows,
class TestObject : INotifyPropertyChanged
{
private string _state;
public string State
{
get
{
return _state;
}
set
{
if (_state == value) return;
_state = value;
OnPropertyChanged("State");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
and in your FormInput
public FormInput()
{
InitializeComponent();
TestObject t = new TestObject();
labelState.DataContext = t;
t.State = "ok";
}
and XAML as follows,
<Label Name="labelState" Content="{Binding State}" HorizontalAlignment="Right" >

ObservableCollection binding to listbox ui doesn't update

I know I should use the MVVM pattern but I'm trying to get step by step closer to it. So here is my Listbox:
<ListBox x:Name="BoardList" ItemsSource="{Binding notes}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<TextBox IsReadOnly="True" ScrollViewer.VerticalScrollBarVisibility="Visible" Text="{Binding text}" TextWrapping="Wrap" Foreground="DarkBlue"></TextBox>
<AppBarButton Visibility="{Binding visibility}" Icon="Globe" Click="OpenInBrowser" x:Name="Link"></AppBarButton>
<AppBarButton Icon="Copy" Click="Copy"></AppBarButton>
<AppBarButton Icon="Delete" Click="Delete"></AppBarButton>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In the Mainpage.xaml.cs I declare the following:
ObservableCollection<BoardNote> notes = new ObservableCollection<BoardNote>();
So if I understood this right I don't need to care about the "INotifyCollectionChanged" stuff because I'm using an observablecollection?
So I got for example a textbox like this:
<Textbox x:Name="UserInputNote" Placeholdertext="Type in a text for your note"></Textbox>
And a button to Add the new note to the ObservableCollection and the click event is just like this:
notes.Add(new BoardNote(UserInputNote.Text));
So now the UI should update every time the user clicks the button to save a new note. But nothing happens. What did I do wrong?
If you need it here is the BoardNote class:
class BoardNote
{
public string text
{
get; set;
}
public BoardNote(string text)
{
this.text = text;
}
public Visibility visibility
{
get
{
if (text.StartsWith("http"))
return Visibility.Visible;
else
return Visibility.Collapsed;
}
}
}
You need to implement INotifyPropertyChanged. Here's one way of doing it.
Create this NotificationObject class.
public class NotificationObject : INotifyPropertyChanged
{
protected void RaisePropertyChanged<T>(Expression<Func<T>> action)
{
var propertyName = GetPropertyName(action);
RaisePropertyChanged(propertyName);
}
private static string GetPropertyName<T>(Expression<Func<T>> action)
{
var expression = (MemberExpression)action.Body;
var propertyName = expression.Member.Name;
return propertyName;
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then your BoardNote class will inherit it this way:
class BoardNote : NotificationObject
{
private string _text
public string Text
{
get {return _text;}
set
{
if(_text == value) return;
_text = value;
RaisePropertyChanged(() => Text);
}
}
public BoardNote(string text)
{
this.text = text;
}
public Visibility visibility
{
get
{
if (text.StartsWith("http"))
return Visibility.Visible;
else
return Visibility.Collapsed;
}
}
}

Label Binding does not work

I am trying to bind a label to an Object.Object.Property and I don't get it run.
Here is my code:
XAML
<Window x:Class="MyApp.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyWindow" Height="1120" Width="800">
<Grid Name="MyGrid">
<StackPanel Orientation="Horizontal">
<Label FontWeight="Bold" FontSize="40" Content="{Binding MyDataObject/AnotherSubObject/MyProperty}"/>
</StackPanel>
</Grid>
</Window>
And the Code:
public partial class MyWindow : Window
{
public MySubObject MyDataObject { get; set; }
public MyWindow(MySubObject object)
{
InitializeComponent();
this.MyDataObject = object; // Contains MyDataObject.AnotherObject.MyProperty
DataContext = this;
}
}
And the code for MySubObject object looks like this:
public class MySubObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
AnotherObject _AnotherObject;
public MySubObject()
{
this._AnotherObject = new AnotherObject();
this._AnotherObject.Property = "Some Value";
}
public AnotherObject AnotherObject
{
get { return _AnotherObject; }
set { _AnotherObject = value; OnPropertyChanged("AnotherObject"); }
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I would be glad to get dome support for this case.
Use Dot(.) as binding property path separator not Forward slash(/)
<Label FontWeight="Bold" FontSize="40"
Content="{Binding MyDataObject.AnotherSubObject.MyProperty}"/>

WPF Binding to a property in second ViewModel

How do I get the text bound to txtMessage from the second view model? When I had only one view model, the text was working fine. It does not work anymore when I moved the actual download code to second view model. Am I missing something? Any help appreciated.
Xaml:
<DockPanel DockPanel.Dock="Top">
<TextBlock x:Name="txtMessage" DockPanel.Dock="Top" Margin="5" Text="{Binding viewModel1.Message}" />
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="5,5">
<ProgressBar Width="300" Visibility="{Binding IsDownloading, Converter={converter:VisibilityConverter}}" IsIndeterminate="True" />
<Button Content="Cancel" />
</StackPanel>
</DockPanel>
<Button Content="Download" Width="120" Margin="0,0,5,0" Name="btnSubmit" Click="btnSubmit_Click" />
CodeBehind:
public partial class DownloadWindow: Window
{
DownloadWindowViewModel viewModel = new DownloadWindowViewModel();
public DownloadWindow()
{
InitializeComponent();
this.DataContext = viewModel;
}
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
viewModel.IsDownloading = true;
viewModel.Download();
}
}
viewModel:
public class DownloadWindowViewModel: INotifyPropertyChanged
{
Thread downloadThread;
public DownloadViewModel viewModel1;
public DownloadWindowViewModel()
{
viewModel1 = new DownloadViewModel();
}
private bool _isDownloading; = false;
public bool IsDownloading
{
get
{
return _isDownloading;
}
set
{
_isDownloading; = value;
OnPropertyChanged("IsDownloading");
}
}
public void Download()
{
viewModel1.Download();
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
viewModel1:
public class DownloadViewModel: INotifyPropertyChanged
{
Thread _thread;
public void Download()
{
ThreadStart threadStart = delegate()
{
StartDownload();
};
_thread = new Thread(threadStart);
_thread.IsBackground = true;
_thread.Start();
}
private void StartDownload()
{
for (int i = 10; i < 1500; i++)
{
Thread.Sleep(5000);
Message = "Downloading " + i.ToString();
}
}
private string _message = "";
public string Message
{
get
{
return _message;
}
set
{
if (_message != value)
{
_message = value;
OnPropertyChanged("Message");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Your viewModel1 has to be a property, and it's a field at the moment. Change it to:
public DownloadViewModel viewModel1 { get; set; }
Explanation why such restriction exists, can be found here (primarily due to notification/verifications mechanisms simply not working for fields):
Why does WPF support binding to properties of an object, but not fields?

Categories