I have posted a previous question but there was little help so I tried to start coding it and looking up some more on my own and I'm stuck on some code. I'm trying to follow MVVM
I have created a Class called Standard which looks like this:
namespace MVVModel
{
public class Standard
{
string _title;
string _question;
public string Title
{
get { return _title; }
set { _title = value; }
}
public string Question
{
get { return _question; }
set { _question = value; }
}
}
}
Then I created ViewModel class which looks like this:
namespace MVVModel
{
class ViewModel
{
ObservableCollection<Standard> _title = new ObservableCollection<Standard>();
ObservableCollection<Standard> _question = new ObservableCollection<Standard>();
public ViewModel()
{
}
public ObservableCollection<Standard> Title
{
get
{
return _title;
}
set
{
_title = value;
}
}
public ObservableCollection<Standard> Question
{
get
{
return _question;
}
set
{
_question = value;
}
}
}
}
Here is my XAML:
<Grid>
<Button x:Name="btnTitle" Content="Title" HorizontalAlignment="Left" Margin="691,22,0,0" VerticalAlignment="Top" Width="75"/>
<Button x:Name="btnQuestion" Content="Question" HorizontalAlignment="Left" Margin="797,22,0,0" VerticalAlignment="Top" Width="75" Command="{Binding AddTitle}"/>
<ItemsControl ItemsSource="{Binding Question}" Margin="0,86,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
I just want to create a textbox dynamically but nothing it showing, any help?
I have mentioned in my previous answer about implementing INotifyPropertyChanged.
Why you again need a collection of Questions and Titles in your viewModel, this already exists in the Standard class.
You need a collection of Standard class in your main ViewModel. Thats what I get from your question if I have understood it correctly.
Here is the implementation for INotifyPropertyChanged
public class Standard : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void NotifyOfPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
protected void NotifyOfPropertyChanged<TProperty>(Expression<Func<TProperty> property)
{
NotifyOfPropertyChanged(property.GetMemberInfo().Name);
}
string _title;
ObservableCollection<string> _questions;
public string Title
{
get { return _title; }
set {
_title = value;
NotifyOfPropertyChanged(()=>Title);
}
}
public ObservableCollection<string> Questions
{
get { return _questions; }
set {
_questions = value;
NotifyOfPropertyChanged(()=>Questions);
}
}
}
You must follow a couple of steps to accomplish this task.
First you need to bind the collection of Standard to the Grid, in place of Question.
Second you need to bind a property of the previous class to the textbox.
Ex:
<DataTemplate>
<TextBox Text="{Binding Question"} />
</DataTemplate>
Edit:
I would like to mention this article to help you:
http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial
Related
I have a TextBlock in XAML that's bound to a property called EditsWarning:
<TextBlock DockPanel.Dock="Top" Text="{Binding EditsWarning, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{DynamicResource Esri_TextBlockRegular}" HorizontalAlignment="Left" FontSize="14" FontWeight="DemiBold" VerticalAlignment="Center" Margin="10,0,10,5" TextWrapping="WrapWithOverflow"/>
The Definition for the EditsWarning Property is here:
public string EditsWarning
{
get { return editsWarningMessage; }
set
{
SetProperty(ref editsWarningMessage, value, () => this.EditsWarning);
}
}
The EditsWarning Property is set to an instance of a class like this:
editsWarning = new OutstandingEditsTextBlock();
editsWarningMessage = editsWarning.EditsWarningMessage.ToString();
And the OutstandingEditsTextBlock class is here, and implements INotifyPropertyChanged
internal class OutstandingEditsTextBlock : INotifyPropertyChanged
{
private string editsWarning;
public OutstandingEditsTextBlock()
{
if (Project.Current.HasEdits)
{
this.editsWarning = "This session/version has outstanding edits.";
}
else
{
this.editsWarning = string.Empty;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public string EditsWarningMessage
{
get { return this.editsWarning; }
set
{
this.editsWarning = value;
this.OnPropertyChanged("EditsWarningMessage");
}
}
public void OnPropertyChanged(string propertyName)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I noticed that I can get it to display either value, however, I can never get it to update in the same debugging session. In fact, it looks like the setter for the public property is never hit.
Can someone please help me figure out what I'm doing wrong?
Thank you.
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.
I recently started to learn the MVVM Pattern and created a simple application to test a few things.
I have a simple View with:
ListBox holding ObservableCollection of Items
Delete Button
New Button
TextBox for Item Description
TextBox for Item Value
Everything works except for the fact that, if i'm updating the item description the ListBox entry isn't updating. I read some articles about this, so i think it has something to do with CollectionChanged isn't called. I tried some possible solutions to this problem, but none of them worked. So maybe there is something generally wrong with my approach.
Hopefully someone can help me with this problem.
Model/Item.cs
internal class Item : INotifyPropertyChanged
{
#region Fields
private string value;
private string description;
#endregion
#region Constructors
public Item()
{
}
public Item(string value, string description) {
this.description = description;
this.value = value;
}
#endregion
public String Value
{
get
{
return value;
}
set
{
this.value = value;
OnPropertyChanged("Value");
}
}
public String Description
{
get
{
return description;
}
set
{
description = value;
OnPropertyChanged("Description");
}
}
#region Overrides
public override string ToString()
{
return description;
}
#endregion String Override
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
ViewModel/MainViewModel.cs
...
private ObservableCollection<Item> items;
private Item selectedItem;
public ObservableCollection<Item> Items {
get
{
return items;
}
set
{
items = value;
OnPropertyChanged("Items");
}
}
public Item SelectedItem {
get
{
return selectedItem;
}
set
{
selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
...
View/MainWindow.xaml
...
<Button Content="New" Command="{Binding NewCommand}" />
<Button Content="Delete" Command="{Binding DeleteCommand}" />
<ListBox x:Name="lbxItems" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" />
<TextBox Text="{Binding SelectedItem.Description}" />
<TextBox Text="{Binding SelectedItem.Value}" />
...
with this ItemTemplate it should work
<ListBox Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Value}" Margin="0,0,10,0/>
<TextBlock Text="{Binding Path=Description}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
How you generate a PropertyChangedEvent in your model class?
try this:
internal class Range : INotifyPropertyChanged
{
private string _value;
public string Value
{
get { return _value; }
set
{
if (_value == value) return;
_value = value;
OnPropertyChanged("Value");
}
}
//Do same for the Description property
//Do not forgot implement INotifyPropertyChanged interface here
}
I hope this help you
Oh! After your update it is clearly. You does not use any datatemplate for listbox item, so WPF calls the ToString() method for show item without DT. But when you update any property, WPF does not know about this because object does not changing.
Use Datatemplate or try call OnPropertyChanged with empty string.
I'm a C# newbie and I'm trying to make a simple program that has two buttons and a label. Buttons are "up" and "down", and the number in the label changes according to them. When I execute the program, label shows the initial value which defined in viewmodel. But when I push buttons, it doesn't update the value. I couldn't figure out which is not working, buttons or label when an update occurs. Thanks for your time.
I think the problem is the usage "RaisePropertyChanged". I have no idea how should I use it. If someone explains it, it would be good.
Buttons and Label in view:
<Button x:Name="Up" Content="Up" HorizontalAlignment="Left" Grid.Row="6" Grid.Column="1" VerticalAlignment="Top" Width="75" Grid.ColumnSpan="2" Command="{Binding UpCommand}"/>
<Button x:Name="Down" Content="Down" Grid.Column="3" HorizontalAlignment="Left" Grid.Row="6" VerticalAlignment="Top" Width="75" Command="{Binding DownCommand}" />
<Label x:Name="Number" HorizontalAlignment="Left" Height="31" Grid.Row="4" VerticalAlignment="Top" Width="161" Content="{Binding Path=Lol, UpdateSourceTrigger=PropertyChanged}" Background="#FFB95151" Grid.ColumnSpan="3" Grid.Column="1" />
this is ViewModel:
class MainViewModel : BaseINPC
{
//Number class to hold the number
private Number lol; //I'm not good at naming
public Number Lol
{
get
{
return lol;
}
set
{
lol = value;
RaisePropertyChanged("Lol");
}
}
public MainViewModel()
{
lol = new Number { number = 5 }; //initial value
RaisePropertyChanged("Lol");
}
#region Commands
//Up botton commands
void UpNumber()
{
lol.change(true); //it adds 1 to number when True, substracts 1 when false
RaisePropertyChanged("Lol");
}
bool CanUpNumber()
{
return true;
}
public DelegateCommand UpCommand { get { return new DelegateCommand(UpNumber, CanUpNumber); } }
//Down button
void DownNumber()
{
lol.change(false);
RaisePropertyChanged("Lol");
}
bool CanDownNumber()
{
return true;
}
public DelegateCommand DownCommand { get { return new DelegateCommand(DownNumber, CanDownNumber); } }
#endregion
}
This is in my Model :
class Number : INotifyPropertyChanged
{
private int _number;
public int number
{
get { return _number; }
set
{
_number = value;
}
}
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public void change(bool up)
{
if (up)
_number++;
else
_number--;
}
public override String ToString()
{
return "" + _number;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
in your Number class
public void change(bool up)
{
if (up)
_number++;
else
_number--;
RaisePropertyChanged("number");
}
or
public int number
{
get { return _number; }
set
{
_number = value;
RaisePropertyChanged("number");
}
}
public void change(bool up)
{
if (up)
number++;
else
number--;
}
you were missing notification for the number property
also change the content binding of Label to Content="{Binding Path=Lol.number}" since number is the real property which actually change when method change() is executed.
Lol may not necessary to be notified if not changing while runtime, also UpdateSourceTrigger=PropertyChanged is unrelevant with binding to label so perhaps remove that too.
I want change number from ComboBox and change value in TextBox.
(For example I have number =2 and content at "lblblblb" ofc. this is in ObservableCollection<string>, so I to call ContentWithListView[SelectNumberStep])
ReadPage.xaml
<TextBox HorizontalAlignment="Left" Margin="580,154,0,0"
TextWrapping="Wrap" Text="{Binding ContentWithListView[SelectNumberStep],Mode=TwoWay}"
VerticalAlignment="Top" Width="725" Height="82"/>
<ComboBox HorizontalAlignment="Left" Margin="440,154,0,0"
ItemsSource="{Binding NumberStep,Mode=TwoWay}"
SelectedItem="{Binding SelectNumberStep,Mode=TwoWay}"
VerticalAlignment="Top" Width="95" Height="77" />
How I change content in TextBox from CombBox numbers?
ReadViewModel.cs
private ObservableCollection<string> contentWithListView;
public ObservableCollection<string> ContentWithListView
{
get
{
return this.contentWithListView;
}
set
{
this.contentWithListView = value;
}
}
private ObservableCollection<int> stepNumber;
public ObservableCollection<int> NumberStep
{
get
{
return this.stepNumber;
}
set
{
this.stepNumber = value;
}
}
private int selectNumberStep;
public int SelectNumberStep
{
get
{
return this.selectNumberStep;
}
set
{
this.selectNumberStep = value;
}
}
previous answer doesn't integrate the fact he textbox content have to be in TwoWays so, in such scenario, you could consolidate your properties with the INotifyPropertyChanged interface like that:
Xaml part
<StackPanel d:DataContext="{d:DesignInstance Type=classes:StackOverFlowX }">
<TextBox Text="{Binding Content, Mode=TwoWay}"/>
<ComboBox ItemsSource="{Binding NumberStep, Mode=TwoWay}"
SelectedItem="{Binding SelectNumberStep,Mode=TwoWay}"/>
</StackPanel>
Class
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class StackOverFlowX : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public StackOverFlowX()
{
}
private ObservableCollection<string> contentWithListView;
public ObservableCollection<string> ContentWithListView
{
get
{
return this.contentWithListView;
}
set
{
this.contentWithListView = value;
OnPropertyChanged();
}
}
private ObservableCollection<int> stepNumber;
public ObservableCollection<int> NumberStep
{
get
{
return this.stepNumber;
}
set
{
this.stepNumber = value;
OnPropertyChanged();
}
}
private int selectNumberStep;
public int SelectNumberStep
{
get
{
return this.selectNumberStep;
}
set
{
this.selectNumberStep = value;
OnPropertyChanged();
OnPropertyChanged("Content");
}
}
private string _content;
public string Content
{
get
{
return contentWithListView[this.SelectNumberStep];
}
set
{
this._content = value;
if (contentWithListView.IndexOf(value) > -1)
{
SelectNumberStep = contentWithListView.IndexOf(value);
OnPropertyChanged("SelectNumberStep");
}
OnPropertyChanged();
}
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I would change your view model code around, so that the text box binds to a scalar string value which is updated on the SelectNumberStep change:
public string Content
{
get
{
// bounds checking here..
return contentWithListView[this.SelectNumberStep];
}
}
public int SelectNumberStep
{
get
{
return this.selectNumberStep;
}
set
{
this.selectNumberStep = value;
this.NotifyOfPropertyChange(() => this.SelectNumberStep);
this.NotifyOfPropertyChange(() => this.Content);
}
}
<TextBox Text="{Binding Content}" ... />