When developing MVVP program i collide with some problem based on unsensibility of class when it's properties changed.
For example we have base class Brick, that have properties inditating Number of Holes and Cracks in it.
class Class_Brick : DependencyObject
{
public Class_Brick()
{
}
#region DependencyProperties
public static DependencyProperty NumberOfHoles_Property = DependencyProperty.Register("NumberOfHoles", typeof(int), typeof(Class_Brick),
new FrameworkPropertyMetadata()
{
DefaultValue = 0,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged
});
public static DependencyProperty NumberOfCracks_Property = DependencyProperty.Register("NumberOfCracks", typeof(int), typeof(Class_Brick),
new FrameworkPropertyMetadata()
{
DefaultValue = 0,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged
});
#endregion
#region Properties
public int NumberOfHoles
{
get { return (int)GetValue(NumberOfHoles_Property); }
set { SetValue(NumberOfHoles_Property, value); }
}
public int NumberOfCracks
{
get { return (int)GetValue(NumberOfCracks_Property); }
set { SetValue(NumberOfCracks_Property, value); }
}
#endregion
}
Class Wall has collection of Bricks and Standard Brick (put both in DependencyProperty is not obligatory - only to demonstrate needed callback).
Also Wall has same properties as Brick that calculate Number of Cracks and Holes in collection of Bricks and Standard Brick. Tey must calculate every time, when changed parametres in Standard Brick or changed collection (add item, remove item or changed parametres in item).
class Class_Wall : DependencyObject
{
public Class_Wall()
{
SetValue(Standard_Brick_Property, new Class_Brick());
SetValue(Bricks_Property, new ObservableCollection<Class_Brick>());
}
#region DependencyProperties
public static DependencyProperty NumberOfHoles_Property = DependencyProperty.Register("NumberOfHoles", typeof(int), typeof(Class_Wall),
new FrameworkPropertyMetadata()
{
DefaultValue = 0,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged
});
public static DependencyProperty NumberOfCracks_Property = DependencyProperty.Register("NumberOfCracks", typeof(int), typeof(Class_Wall),
new FrameworkPropertyMetadata()
{
DefaultValue = 0,
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged
});
public static DependencyProperty Standard_Brick_Property = DependencyProperty.Register("Standard_Brick", typeof(Class_Brick), typeof(Class_Wall),
new FrameworkPropertyMetadata()
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged,
PropertyChangedCallback = new System.Windows.PropertyChangedCallback(On_Property_Change)
});
public static DependencyProperty Bricks_Property = DependencyProperty.Register("Bricks", typeof(ObservableCollection<Class_Brick>), typeof(Class_Wall),
new FrameworkPropertyMetadata()
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = System.Windows.Data.UpdateSourceTrigger.PropertyChanged,
PropertyChangedCallback = new System.Windows.PropertyChangedCallback(On_Property_Change)
});
#endregion
#region Inner Methods
public static void On_Property_Change(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ObservableCollection<Class_Brick> inWall;
int wallCracks = 0;
int wallHoles = 0;
if (e.Property == Bricks_Property)
{
inWall = e.NewValue as ObservableCollection<Class_Brick>;
Class_Brick mainItem = (Class_Brick)d.GetValue(Standard_Brick_Property);
wallCracks = mainItem.NumberOfCracks;
wallHoles = mainItem.NumberOfHoles;
}
else //if (e.Property == Standard_Brick_Property)
{
inWall = (ObservableCollection<Class_Brick>)d.GetValue(Bricks_Property);
Class_Brick newItem = e.NewValue as Class_Brick;
wallCracks = newItem.NumberOfCracks;
wallHoles = newItem.NumberOfHoles;
}
if (inWall != null)
{
foreach (Class_Brick brick in inWall)
{
wallCracks += brick.NumberOfCracks;
wallHoles += brick.NumberOfHoles;
}
d.SetValue(Home_Quest.Class_Wall.NumberOfCracks_Property, wallCracks);
d.SetValue(Home_Quest.Class_Wall.NumberOfHoles_Property, wallHoles);
}
}
#endregion
#region Properties
public int NumberOfHoles
{
get { return (int)GetValue(NumberOfHoles_Property); }
}
public int NumberOfCracks
{
get { return (int)GetValue(NumberOfCracks_Property); }
}
public Class_Brick Standard_Brick
{
get { return (Class_Brick)GetValue(Standard_Brick_Property); }
set { SetValue(Standard_Brick_Property, value); }
}
public ObservableCollection<Class_Brick> Bricks
{
get { return (ObservableCollection<Class_Brick>)GetValue(Bricks_Property); }
set { SetValue(Bricks_Property, value); }
}
#endregion
}
I used this XAML for the test field:
<DockPanel LastChildFill="True">
<TextBlock Text="Wall"
DockPanel.Dock="Top"
HorizontalAlignment="Center"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Column="0"
Grid.Row="0" Grid.RowSpan="2"
LastChildFill="True">
<TextBlock Text="Bricks"
DockPanel.Dock="Top" />
<Button Content="-" DockPanel.Dock="Bottom"
Click="btn_Remove" />
<Button Content="+" DockPanel.Dock="Bottom"
Click="btn_Add" />
<ListBox x:Name="lB_Bricks"
ItemsSource="{Binding Path=Bricks}"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="2"
BorderBrush="Azure"
BorderThickness="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Holes"
Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding Path=NumberOfHoles, Mode=TwoWay}"
Grid.Column="1" Grid.Row="0"
TextAlignment="Center"/>
<TextBlock Text="Cracks"
Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding Path=NumberOfCracks, Mode=TwoWay}"
Grid.Column="1" Grid.Row="1"
TextAlignment="Center"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
<DockPanel Grid.Column="1"
Grid.Row="0"
LastChildFill="True"
DataContext="{Binding Path=Standard_Brick}">
<TextBlock Text="Standard Brick"
DockPanel.Dock="Top" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="Holes"
Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding Path=NumberOfHoles, Mode=TwoWay}"
Grid.Column="1" Grid.Row="0"
TextAlignment="Center"/>
<TextBlock Text="Cracks"
Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding Path=NumberOfCracks, Mode=TwoWay}"
Grid.Column="1" Grid.Row="1"
TextAlignment="Center"/>
</Grid>
</DockPanel>
<DockPanel Grid.Column="1"
Grid.Row="1"
LastChildFill="True">
<TextBlock Text="Resulted Cracks and Holes"
DockPanel.Dock="Top" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="Holes"
Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding Path=NumberOfHoles}"
Grid.Column="1" Grid.Row="0"
IsReadOnly="True"
TextAlignment="Center"/>
<TextBlock Text="Cracks"
Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding Path=NumberOfCracks}"
Grid.Column="1" Grid.Row="1"
IsReadOnly="True"
TextAlignment="Center"/>
</Grid>
</DockPanel>
</Grid>
</DockPanel>
and code for MainWindow.cs:
public MainWindow()
{
InitializeComponent();
this.DataContext = currentWall;
}
Class_Wall currentWall = new Class_Wall();
private void btn_Add(object sender, RoutedEventArgs e)
{
currentWall.Bricks.Add(new Class_Brick());
}
private void btn_Remove(object sender, RoutedEventArgs e)
{
Class_Brick selectedBrick = this.lB_Bricks.SelectedItem as Class_Brick;
if ( selectedBrick != null)
{
currentWall.Bricks.Remove(selectedBrick);
}
}
Until today, I never got to find the solution for this problem, using DependencyProperties.
Hope for your help and support.
Well, INotifyPropertyChanged helps solve my problem.
Here some code for updated Classes Brick:
class Class_Brick_NPC : INotifyPropertyChanged
{
public Class_Brick_NPC()
{
}
#region Event Functions
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
#region Inner Parametres
private int numberOfHoles = 0;
private int numberOfCracks = 0;
#endregion
#region Parametres
public int NumberOfHoles
{
get { return numberOfHoles; }
set
{
numberOfHoles = value;
OnPropertyChanged("NumberOfHoles");
}
}
public int NumberOfCracks
{
get { return numberOfCracks; }
set
{
numberOfCracks = value;
OnPropertyChanged("NumberOfCracks");
}
}
#endregion
}
and Wall:
class Class_Wall_NPC : INotifyPropertyChanged
{
public Class_Wall_NPC()
{
standard_Brick.PropertyChanged += InnerUpdate;
}
#region Event Methods
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public void InnerUpdate(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "NumberOfHoles")
{
OnPropertyChanged("NumberOfHoles");
}
else if (args.PropertyName == "NumberOfCracks")
{
OnPropertyChanged("NumberOfCracks");
}
}
#endregion
#region Inner Properties
private ObservableCollection<Class_Brick_NPC> bricks = new ObservableCollection<Class_Brick_NPC>();
private Class_Brick_NPC standard_Brick = new Class_Brick_NPC();
private int numberOfHoles = 0;
private int numberOfCracks = 0;
#endregion
#region Properties
public ObservableCollection<Class_Brick_NPC> Bricks
{
get { return bricks; }
set
{
bricks = value;
OnPropertyChanged("Bricks");
}
}
public Class_Brick_NPC Standard_Brick
{
get { return standard_Brick; }
set
{
standard_Brick = value;
}
}
public int NumberOfHoles
{
get
{
int wallHoles = standard_Brick.NumberOfHoles;
foreach (Class_Brick_NPC brick in bricks)
{
wallHoles += brick.NumberOfHoles;
}
return wallHoles;
}
}
public int NumberOfCracks
{
get
{
int wallCracks = standard_Brick.NumberOfCracks;
foreach (Class_Brick_NPC brick in bricks)
{
wallCracks += brick.NumberOfCracks;
}
return wallCracks;
}
}
#endregion
#region Methods
public void Add_Brick(Class_Brick_NPC addedItem)
{
bricks.Add(addedItem);
addedItem.PropertyChanged += InnerUpdate;
OnPropertyChanged("Bricks");
}
public void Remove_Brick(Class_Brick_NPC removedItem)
{
bricks.Remove(removedItem);
removedItem.PropertyChanged -= InnerUpdate;
OnPropertyChanged("Bricks");
OnPropertyChanged("NumberOfHoles");
OnPropertyChanged("NumberOfCracks");
}
#endregion
}
But is there any way to achieve same result using DependencyProperty?
Related
I'm trying to dev a WPF app which displays 3 views that we can switch using a side menu.
The first View displays articles. I've tried this view in another project on its own and it works just fine.
My issue:
The problem is that when I use it in my navigation (with the exact same code) the Images or Titles won't appear unless I click on another view first then click back.
Here's my current code:
Model: (Article)
public class Article : INotifyPropertyChanged
{
int id;
string title;
string link;
BitmapImage image;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public Article(int id, string title, string link, BitmapImage image)
{
Id = id;
Title = title;
Link = link;
Image = image;
}
public int Id
{
get => id;
set { if (value != id) { id = value; NotifyPropertyChanged(); } }
}
public string Title
{
get => title;
set { if (value != title) { title = value; NotifyPropertyChanged(); } }
}
public string Link
{
get => link;
set { if (value != link) { link = value; NotifyPropertyChanged(); } }
}
public BitmapImage Image
{
get => image;
set { if (value != image) { image = value; NotifyPropertyChanged(); } }
}
}
ViewModel: (ArticlesViewModel)
public class ArticlesViewModel : INotifyPropertyChanged
{
Article[] articles;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public AccueilViewModel()
{
loadArticle();
}
public Article[] Articles
{
get => articles;
set { if (value != articles) { articles = value; NotifyPropertyChanged(); } }
}
private async void loadArticle()
{
var client = new WordPressClient(App.WordpressLink);
try
{
var asychArticles = GetArticles(client);
await asychArticles;
var articles = asychArticles.Result;
int i = 0;
int a;
foreach (WordPressPCL.Models.Post p in articles)
{
a = p.FeaturedMedia.Value;
var media = GetMedia(client, a);
await media;
BitmapImage bimage = App.getImage(media.Result.SourceUrl);
articleImg[i].Source = bimage;
articleTitle[i].Content = p.Title.Rendered.ToUpper();
articleLink[i] = p.Link;
i++;
if (i == 4) break;
}
}
catch (System.Net.Http.HttpRequestException e)
{
}
}
static async Task<IEnumerable<WordPressPCL.Models.Post>> GetArticles(WordPressClient client)
{
var posts = await client.Posts.GetAll();
return posts;
}
static async Task<WordPressPCL.Models.MediaItem> GetMedia(WordPressClient client, int number)
{
var media = await client.Media.GetByID(number);
return media;
}
}
View: (ArticlesView)
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="5*"/>
</Grid.RowDefinitions>
<Grid Name="Article1" Grid.Row="0" Margin="10,10,10,10" DataContext="{Binding Articles[0]}">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image Name="Image1" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
<Button Name="Titre1" Grid.Row="1" Content="{Binding Title}"/>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Name="Article2" Grid.Column="0" Margin="10,10,10,10" DataContext="{Binding Articles[1]}">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image Name="Image2" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
<Button Name="Titre2" Grid.Row="1" Content="{Binding Title}"/>
</Grid>
<Grid Name="Article3" Grid.Column="1" Margin="10,10,10,10" DataContext="{Binding Articles[2]}">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image Name="Image3" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
<Button Name="Titre3" Grid.Row="1" Content="{Binding Title}"/>
</Grid>
<Grid Name="Article4" Grid.Column="2" Margin="10,10,10,10" DataContext="{Binding Articles[3]}">
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Image Name="Image4" Grid.Row="0" Stretch="UniformToFill" Source="{Binding Image}"/>
<Button Name="Titre4" Grid.Row="1" Content="{Binding Title}"/>
</Grid>
</Grid>
</Grid>
MainWindow.xaml:
<Window.Resources>
<DataTemplate x:Name="ArticlesViewTemplate" DataType="{x:Type viewmodels:ArticlesViewModel}">
<views:ArticlesView DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="Feature2ViewTemplate" DataType="{x:Type viewmodels:Feature2ViewModel}">
<views:Feature2View DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="Feature3ViewTemplate" DataType="{x:Type viewmodels:Feature3ViewModel}">
<views:Feature3View DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="4*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<StackPanel Grid.Row="1" VerticalAlignment="Top" Margin="20,20,20,0">
<Button Content="Articles" Click="F1Button_Click"/>
<Button Content="F2" Click="F2Button_Click"/>
<Button Content="F3" Click="F3Button_Click"/>
</StackPanel>
</Grid>
<ContentControl Grid.Column="1" Content="{Binding}"/>
</Grid>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
ArticlesViewModel avm;
Feature2ViewModel f2vm;
Feature3ViewModel f3vm;
public MainWindow()
{
InitializeComponent();
}
private void F1Button_Click(object sender, RoutedEventArgs e)
{
if (avm == null) avm = new AccueilViewModel();
DataContext = avm;
}
private void F2Button_Click(object sender, RoutedEventArgs e)
{
if (f2vm == null) f2vm = new CatalogueViewModel();
DataContext = f2vm;
}
private void F3Button_Click(object sender, RoutedEventArgs e)
{
if (f3vm == null) f3vm = new MesFormationsViewModel();
DataContext = f3vm;
}
}
To sum up:
Click on Articles -> Nothing is displayed
Click on F2 then Articles -> Nothing is displayed
Click on Articles then F2 then Articles -> Images and Titles correctly displayed!
Any ideas on where the problem might be coming from? Thank you.
So the problem seemed to be coming from the fact that I was using an array. At the start the DataContext was null so the images and titles even though they were loaded in the modelview were not displayed on the view.
The problem was fixed by using an ObservableCollection instead of the array. If I understood correctly, NotifyPropertyChanged() is apparently not notifying the view when the array is at first null.(?)
In my code I have one observable collection that has many combo boxes inside that. Now I have to add List for each combo boxes using MVVM (Model-View-View Model) i.e., No code behind
In View.xaml:
<Window.... xmlns:VM="clr-namespace:myproject.myViewModel"
... >
<Window.DataContext><VM:myViewModel/>
</Window.DataContext>
<ItemsControl ItemsSource="{Binding myCollection}" >
<ItemsControl.ItemTemplate >
<DataTemplate>
<DockPanel>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Margin="0,20,0,0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="City" Margin="20" VerticalAlignment="Center"/>
<ComboBox KeyboardNavigation.TabIndex="0" Grid.Column="1" Margin="45,10,10,10" Height="30" Width="200" ItemsSource="{Binding City}" />
<TextBlock Text="temperature" Grid.Row="1" VerticalAlignment="Center" Margin="20" />
<ComboBox KeyboardNavigation.TabIndex="3" Grid.Column="1" Grid.Row="1" Margin="45,20,10,10" Height="30" Width="200" SelectedIndex="0" HorizontalContentAlignment="Right"
VerticalAlignment="Center" ItemsSource="{Binding Temperature}">
</ComboBox>
<TextBlock Text="State" Grid.Row="1" VerticalAlignment="Center" Margin="10" Grid.Column="2"/>
<ComboBox KeyboardNavigation.TabIndex="3" Grid.Column="3" Grid.Row="1" Margin="10" Height="30" Width="200" HorizontalContentAlignment="Center" ItemsSource="{Binding State}" >
</ComboBox>
<TextBlock Text="Open Files " VerticalAlignment="Center" Grid.Row="0" Grid.Column="2" Margin="10" />
<TextBox Grid.Column="3" Text="" Height="30" Grid.Row="0" IsReadOnly="True"
TextAlignment="Right" VerticalContentAlignment="Center" Width="200" /> <Button Grid.Column="4" Content="Browse" Height="30" VerticalAlignment="Bottom" MinWidth="41" />
</Grid>
</Window>
In **Model.cs**:
namespace myproject.Models
{
public class projectmodel : INotifyPropertyChanged
{
private ObservableCollection<projectmodel> city;
private ObservableCollection<projectmodel> temperature;
private ObservableCollection<projectmodel> state;
public ObservableCollection<projectmodel> City
{
get { return city; }
set
{
city = value;
NotifyPropertyChanged("City");
}
}
public ObservableCollection<projectmodel> Temperature
{
get { return temperature; }
set
{
temperature = value;
NotifyPropertyChanged("Temperature");
}
}
public ObservableCollection<projectmodel> State
{
get { return state; }
set
{
state = value;
NotifyPropertyChanged("State");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Private Helpers
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}}
In ViewModel.cs:
namespace myproject.ViewModels
{
public class projectViewModel
{
public ObservableCollection<T> myCollection
{
get; set;
}
public projectViewModel()
{
myCollection = new ObservableCollection<T>();
List<string> lstCity = new List<string>();
lstCity = new List<string> { "Coimbatore", "Chennai", "Bangalore" };
List<string> lstTemperature = new List<string>();
lstTemperature = new List<string> { "17c", "18c", "15c" };
List<string> lstState = new List<string>();
lstState = new List<string> { "Andhra", "karnataka", "TamilNadu" };
}
}myCollection.Add(new projectmodel
{
City = lstCity.ToArray(),
Temperature = lstTemperature.ToArray(),
State= lstState.ToArray()
});
}
}}
This is my code,I didn't get anything if I select my combo boxes. Please suggest me how should I write my viewmodel.cs ,and also correct me if I'm wrong anywhere else.
You have to declare all your collections inside your projectmodel to be of type ObservableCollection<string> instead of sting[]. When ever you bind to a collection use ObservableCollection<T>.
Your view models must also all implement INotifyPropertyChanged
Changes:
ProjectModel:
namespace Myproject.Models
{
public class ProjectModel : INotifyPropertyChanged
{
private ObservableCollection<string> city;
private ObservableCollection<string> temperature;
private ObservableCollection<string> state;
public ObservableCollection<string> City
{
get { return city; }
set
{
city = value;
NotifyPropertyChanged("City");
}
}
public ObservableCollection<string> Temperature
{
get { return temperature; }
set
{
temperature = value;
NotifyPropertyChanged("Temperature");
}
}
public ObservableCollection<string> State
{
get { return state; }
set
{
state = value;
NotifyPropertyChanged("State");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Private Helpers
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
ProjectViewModel:
namespace MyProject.ViewModels
{
public class ProjectViewModel : INotifyPropertyChanged
{
private ObservableCollection<Projectmodel> myCollection;
public ObservableCollection<Projectmodel> MyCollection
{
get => this.myCollection;
set
{
if (Equals(value, this.myCollection)) return;
this.myCollection = value;
OnPropertyChanged();
}
}
public ProjectViewModel()
{
MyCollection = new ObservableCollection<Projectmodel>()
{
new ProjectModel()
{
City = new ObservableCollection<string>()
{
"Coimbatore", "Chennai", "Bangalore"
},
Temperature = new ObservableCollection<string>()
{
"17c", "18c", "15c"
},
State = new ObservableCollection<string>()
{
"Andhra", "karnataka", "TamilNadu"
}
}
};
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Edit:
I updated the code to meet the common naming conventions using PascalCase. It is recommended to follow them: C# Conventions
I'm trying to display a model and I want to save it when the user clicks a button.
(Bind ModelView OneWay to Model and if the save button was pressed I update the source manually.)
Is that possible?
Here I created a small example with my problem. (not MVVM!)
MainWindow.xaml
<Grid>
<Grid.DataContext>
<ViewModel:MainViewModel/>
</Grid.DataContext>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<local:PersonListView Grid.Column="0" DataContext="{Binding ViewModel_PersonList}"/>
<local:PersonView Grid.Column="1" DataContext="{Binding ViewModel_Person}"/>
</Grid>
PersonListView.xaml
<ListView ItemsSource="{Binding PersonList}" SelectedItem="{Binding SelectedPerson}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding LastName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
PersonView.xaml
<Grid DataContext="{Binding}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Text="First Name:" Grid.Column="0" Grid.Row="0" Margin="5"/>
<TextBlock Text="Last Name:" Grid.Column="0" Grid.Row="1" Margin="5"/>
<TextBlock Text="Age:" Grid.Column="0" Grid.Row="2" Margin="5"/>
<TextBox Text="{Binding Path=Person.FirstName, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="0" Margin="5"/>
<TextBox Text="{Binding Path=Person.LastName, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="1" Margin="5"/>
<TextBox Text="{Binding Path=Person.Age, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="2" Margin="5"/>
<Button Content="Save" Command="{Binding SaveCommand}" Grid.Column="1" Grid.Row="4"/>
</Grid>
Person.cs
public class Person : INotifyPropertyChanged
{
private string firstName;
public string FirstName
{
get { return firstName; }
set
{
firstName = value;
OnPropertyChanged("FirstName");
}
}
private string lastName;
public string LastName
{
get { return lastName; }
set
{
lastName = value;
OnPropertyChanged("LastName");
}
}
private int age;
public int Age
{
get { return age; }
set
{
age = value;
OnPropertyChanged("Age");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
MainViewModel.cs
public class MainViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
ViewModel_PersonList.PersonList.Add(new Model.Person()
{
LastName = "Test",
FirstName = "John",
Age = 20
});
ViewModel_PersonList.PersonList.Add(new Model.Person()
{
LastName = "Example",
FirstName = "Luke",
Age = 25
});
ViewModel_PersonList.SelectedPerson = ViewModel_PersonList.PersonList[1];
ViewModel_Person.Person = ViewModel_PersonList.SelectedPerson;
}
ViewModel.PersonViewModel _ViewModel_Person = new ViewModel.PersonViewModel();
public ViewModel.PersonViewModel ViewModel_Person
{
get
{
return _ViewModel_Person;
}
set
{
_ViewModel_Person = value;
RaisePropertyChanged("ViewModel_Person");
}
}
ViewModel.PersonListViewModel _ViewModel_PersonList = new ViewModel.PersonListViewModel();
public ViewModel.PersonListViewModel ViewModel_PersonList
{
get
{
return _ViewModel_PersonList;
}
set
{
_ViewModel_PersonList = value;
RaisePropertyChanged("ViewModel_PersonList");
}
}
}
PersonListViewModel.css
public class PersonListViewModel : ViewModelBase
{
ObservableCollection<Model.Person> _PersonList = new ObservableCollection<Model.Person>();
public ObservableCollection<Model.Person> PersonList
{
get
{
return _PersonList;
}
set
{
_PersonList = value;
RaisePropertyChanged("PersonList");
}
}
Model.Person _SelectedPerson = new Model.Person();
public Model.Person SelectedPerson
{
get
{
return _SelectedPerson;
}
set
{
MessengerInstance.Send<Model.Person>(value);
_SelectedPerson = value;
RaisePropertyChanged("SelectedPerson");
}
}
}
PersonViewModel.cs
public class PersonViewModel : ViewModelBase
{
public PersonViewModel()
{
MessengerInstance.Register<Model.Person>(this, per => Person = per);
}
Model.Person _Person = new Model.Person();
public Model.Person Person
{
get
{
return _Person;
}
set
{
_Person = value;
RaisePropertyChanged("Person");
}
}
private RelayCommand<Model.Person> _SaveCommand;
public ICommand SaveCommand
{
get
{
if (_SaveCommand == null)
_SaveCommand = new RelayCommand<Model.Person>(SaveCommand_Execute, SaveCommand_CanExecute);
return _SaveCommand;
}
}
private bool SaveCommand_CanExecute(Model.Person newPerson)
{
return true;
}
private void SaveCommand_Execute(Model.Person newPerson)
{
// Do my saving!
}
}
Thank you!
You can make the Mode=TwoWay and use UpdateSourceTrigger=Explicit. Refer below code.
<StackPanel>
<TextBox Name="tb_1" Text="HelloWorld!"/>
<TextBox Name="tb_2" Text="{Binding Path=Text, ElementName=tb_1, Mode=TwoWay,UpdateSourceTrigger=Explicit}"/>
<Button Content="Update Source" Click="Button_Click"/>
</StackPanel>
As the title says, im trying to display the hardcoded values I typed in my XAML file through databindings but in 1 method call instead of binding the textboxes in every specific property.
Model:
public class Person
{
public string Namn { get; set; }
public DateTime Födelsedatum { get; set; }
public int Betyg { get; set; }
public int AntalBarn { get; set; }
public int Favoritsiffra { get; set; }
public string Kommentar { get; set; }
}
View:
<Window x:Class="PiedPiper.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Inlämningsuppgift WPF" Height="400" Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Content="Personuppgifter" FontSize="35" Grid.Column="0" Grid.Row="0"></Label>
<Separator Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="10,2,-50,2"/>
<Label Content="Namn:" Grid.Column="0" Grid.Row="2" Margin="5,5,210,0"></Label>
<TextBox Name="NamnTextBox" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="118,5,-50,0"/>
<Label Content="Födelsedatum:" Grid.Column="0" Grid.Row="3" Margin="5,5,210,0"></Label>
<TextBox Name="FödelsedatumTextBox" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Margin="118,5,-50,0"></TextBox>
<Label Content="Betyg" Grid.Column="0" Grid.Row="4" Margin="5,5,210,0"></Label>
<ComboBox Name="BetygComboBox" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Margin="218,5,-50,0"></ComboBox>
<Label Content="Antal barn (0-20):" Grid.Column="0" Grid.Row="5" Margin="5,5,210,0"></Label>
<Slider Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Margin="125,5,25,0"></Slider>
<TextBox Name="AntalBarnTextBox" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Margin="300,5,-50,0"></TextBox>
<Label Content="Favoritsiffra (0-99):" Grid.Column="0" Grid.Row="6" Margin="5,5,210,0"></Label>
<TextBox Name="FavoritsiffraTextBox" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Margin="225,5,-50,0"></TextBox>
<CheckBox Content="Visa kommentar" Grid.Row="7" Grid.Column="0" Margin="5,5,210,0"></CheckBox>
<TextBox Name="KommentarTextBox" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" Margin="125,5,-50,-50" Grid.RowSpan="2"></TextBox>
<Button Content="Spara" Grid.Row="8" Grid.Column="0" Margin="125,55,-50,-80" Grid.ColumnSpan="2"></Button>
<Separator Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" Margin="10,85,-50,-80"></Separator>
<Label Content="Andre Kordasti" Grid.Row="10" Grid.Column="0" Margin="10,85,-50,-80"></Label>
<Button Content="Avsluta" Grid.Row="10" Grid.Column="0" Margin="225,85,-50,-80" Grid.ColumnSpan="2"></Button>
</Grid>
ViewModel:
public class PersonViewModel : INotifyPropertyChanged
{
public PersonViewModel(Person person)
{
Namn = person.Namn;
Födelsedatum = person.Födelsedatum;
Betyg = person.Betyg;
AntalBarn = person.AntalBarn;
Favoritsiffra = person.Favoritsiffra;
Kommentar = person.Kommentar;
GetPerson();
}
private void GetPerson()
{
Namn = "KurtSune";
Födelsedatum = new DateTime(1980, 09, 06);
Betyg = 3;
AntalBarn = 7;
Favoritsiffra = 10;
Kommentar = "Kommentar...";
var mainWindow = new MainWindow();
mainWindow.NamnTextBox.Text = Namn;
mainWindow.FödelsedatumTextBox.Text = Convert.ToString(Födelsedatum);
mainWindow.BetygComboBox.SelectedValue = Betyg;
mainWindow.AntalBarnTextBox.Text = Convert.ToString(AntalBarn);
mainWindow.FavoritsiffraTextBox.Text = Convert.ToString(Favoritsiffra);
mainWindow.KommentarTextBox.Text = Kommentar;
}
public ICommand SaveCommand { get; set; }
public ICommand AbortCommand { get; set; }
public bool CanSave
{
get
{
if (Namn == null)
{
return false;
}
return !String.IsNullOrWhiteSpace(Namn);
}
}
private string _namn;
public string Namn
{
get { return _namn; }
set { _namn = value; OnPropertyChanged("Namn"); }
}
private DateTime _födelsedatum;
public DateTime Födelsedatum
{
get { return _födelsedatum; }
set { _födelsedatum = value; OnPropertyChanged("Födelsedatum"); }
}
private int _betyg;
public int Betyg
{
get { return _betyg; }
set { _betyg = value; OnPropertyChanged("Betyg"); }
}
private int _antalBarn;
public int AntalBarn
{
get { return _antalBarn; }
set { _antalBarn = value; OnPropertyChanged("AntalBarn"); }
}
private int _favoritSiffra;
public int Favoritsiffra
{
get { return _favoritSiffra; }
set { _favoritSiffra = value; OnPropertyChanged("Födelsedatum"); }
}
private string _kommentar;
public string Kommentar
{
get { return _kommentar; }
set { _kommentar = value; OnPropertyChanged("Kommentar"); }
}
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
And to add my App.xaml.cs file:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var person = new Person();
var personViewModel = new PersonViewModel(person);
var mainWindow = new MainWindow();
mainWindow.DataContext = personViewModel;
mainWindow.Show();
}
}
So I dont get the following values to be displayed... Please help, Iam new to this.
Here is a simplified example where,
I made a DataTemplate where controls are bound to the Person properties. Benefits are obvious because as you will see whichever end is modified, the other will know/show these changes.
(added a button and a MessageBox to further demonstrate this)
Code:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
var person = new Person {Name = "name1", Address = "address1"};
person.PropertyChanged += person_PropertyChanged;
DataContext = person;
}
private void person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
MessageBox.Show("data changed");
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var dataContext = (Person) DataContext;
dataContext.Name = "newName";
dataContext.Address = "newAddress";
}
}
internal class Person : INotifyPropertyChanged
{
private string _address;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public string Address
{
get { return _address; }
set
{
_address = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow"
Width="525"
Height="350">
<Window.Resources>
<DataTemplate x:Key="PersonTemplate" DataType="wpfApplication1:Person">
<StackPanel>
<TextBlock Text="Name" />
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Address" />
<TextBox Text="{Binding Address, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<Button Click="ButtonBase_OnClick" Content="Modify" />
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource PersonTemplate}" />
</StackPanel>
</Grid>
</Window>
Now as you can see there are no hard-coded values in XAML, the object I have passed to DataContext has sample values instead.
I have grid like this:
<Grid x:Name="LayoutRoot" Background="Transparent" DataContext="{Binding}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="CurrentPoints" Grid.Row="1" Grid.Column="0"
Text="{Binding CurrentPoints}" Width="100" Height="100" Foreground="Red">
</TextBlock>
<TextBlock x:Name="PointsLeft" Grid.Row="1" Grid.Column="1"
Text="{Binding AllPoints}" Width="100" Height="100" Foreground="Red" />
<TextBlock x:Name="AllPoints" Grid.Row="1" Grid.Column="2"
Text="{Binding EndPoints}" Grid.ColumnSpan="1"/>
</Grid>
Code for page like this:
Counter pocitadlo = new Counter(200);
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
LayoutRoot.DataContext = pocitadlo;
}
And this is my counter class:
class Counter : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _currentPoints;
public int CurrentPoints
{
get
{
return _currentPoints;
}
set
{
this._currentPoints = value;
onPropertyChanged(this, "CurrentPoints");
}
}
private int _allPoints;
public int AllPoints
{
get
{
return _allPoints;
}
set
{
this._allPoints = value;
onPropertyChanged(this, "AllPoints");
}
}
private int _endPoints;
public int EndPoints
{
get
{
return _endPoints;
}
set
{
this._endPoints = value;
onPropertyChanged(this, "EndPoints");
}
}
public Counter(int endPoints)
{
this._allPoints = 0;
this._currentPoints = 0;
this._endPoints = endPoints;
}
private void onPropertyChanged(object sender, string property)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(sender, new PropertyChangedEventArgs(property));
}
}
}
Why isn´t binding working? What I am missing? Thanks