Hi after i bind a combobox observablecollection , i want to add from a textbox to combobx.
adn it give me this error :
System.NullReferenceException: 'Object reference not set to an instance of an object.'
WpfApp1.MainWindow.getModel(...) returned null.
Image of the ERROR
Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
namespace WpfApp1
{
public class Parts : Changed
{
public string name;
public string Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
RaisePropertyChanged("Name");
}
}
}
}
}
viewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfApp1
{
public class AddViewModel : Changed
{
private ObservableCollection<Parts> _persons;
public string names;
public AddViewModel()
{
Persons = new ObservableCollection<Parts>()
{
new Parts{Name="Nirav"}
,new Parts{Name="Kapil"}
,new Parts{Name="Arvind"}
,new Parts{Name="Rajan"}
};
}
public ObservableCollection<Parts> Persons
{
get { return _persons; }
set {
if (_persons != value)
{
_persons = value;
RaisePropertyChanged("Persons");
}
}
}
private Parts _sperson;
public Parts SPerson
{
get { return _sperson; }
set {
if (_sperson != value)
{
_sperson = value;
RaisePropertyChanged("SPerson");
}
}
}
}
}
MainWindow:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public AddViewModel addviewmodel;
public MainWindow()
{
InitializeComponent();
DataContext = new AddViewModel();
}
public AddViewModel getModel()
{
return addviewmodel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
getModel().Persons.Add(new Parts { Name = cmbtxt.Text});
}
}
}
XAML:
<Grid>
<ComboBox ItemsSource="{Binding Persons}" SelectedItem="{Binding Path=SPersons,Mode=TwoWay}" HorizontalAlignment="Left" Margin="391,17,0,0" VerticalAlignment="Top" Width="314" Height="27">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox Name="cmbtxt" HorizontalAlignment="Left" Height="23" Margin="24,21,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="172" />
<Button Content="Add" HorizontalAlignment="Left" Margin="24,88,0,0" VerticalAlignment="Top" Width="156" Height="49" Click="Button_Click"/>
</Grid>
You could make addviewmodel a private readonly field and initialize it immediately. Then you simply have to set the DataContext to the field in the constructor.
Also, getModel() isn't very C#/.NET friendly. Use a property if you need to expose the field:
public partial class MainWindow : Window
{
private readonly AddViewModel addviewmodel = new AddViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = addviewmodel;
}
public AddViewModel AddViewModel => addviewmodel;
private void Button_Click(object sender, RoutedEventArgs e)
{
addviewmodel.Persons.Add(new Parts { Name = cmbtxt.Text });
}
}
Using a property you can actually remove the field altogether:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = AddViewModel;
}
public AddViewModel AddViewModel { get; } = new AddViewModel();
private void Button_Click(object sender, RoutedEventArgs e)
{
AddViewModel.Persons.Add(new Parts { Name = cmbtxt.Text });
}
}
In MainWindow, you never set a value to addviewmodel, hence it is null. You can fix it by changing your constructor:
public MainWindow()
{
InitializeComponent();
addviewmodel = new AddViewModel()
DataContext = addviewmodel ;
}
Here is a posting about NullReferenceException in general: What is a NullReferenceException, and how do I fix it?
Related
Currently i am trying to learn WPF, but i have hit a brickwall with my current Problem, after many hours googling and trying to fix it on my own. I am trying to display the Model Province. I have found multiple similar Problems but i couldn't figure it out on my own. After having checked the Output there was no mention of any error. Currently the Window shows just the empty Model but no data even though the Observable Collection gets updated. So before i completely destroy my interest in WPF i am asking for help.
MyView
<Window x:Class="isnuaatest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:isnuaatest"
xmlns:local1="clr-namespace:isnuaatest.Models"
xmlns:local2="clr-namespace:isnuaatest.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local2:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid>
<DataGrid ItemsSource="{Binding Provinces, UpdateSourceTrigger=PropertyChanged}">
</DataGrid>
</Grid>
<StackPanel Width="200" Margin="50">
<Button x:Name="OpenSaveFile" Click="OpenSaveFile_Click">OpenSaveFile</Button>
</StackPanel>
</Grid>
My View Model
using isnuaatest.Helper;
using isnuaatest.Models;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Input;
namespace isnuaatest.ViewModel
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public ObservableCollection<Province> _province;
public ObservableCollection<Province> Provinces
{
get { return this._province; }
set
{
_province = value;
}
}
public MainWindowViewModel() : base()
{
this.Provinces = new ObservableCollection<Province>();
}
private string _savegamePath;
public string SavegamePath
{
get { return _savegamePath; }
set { _savegamePath = value; OnPropertyChanged("SavegamePath"); GetProvinces(_savegamePath);}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var savegamefile = this.PropertyChanged;
if (savegamefile != null)
savegamefile(this, new PropertyChangedEventArgs(propertyName));
}
public event EventHandler OnItemChanged;
public void GetProvinces(string path)
{
Reader reader = new Reader();
if (_savegamePath != null)
{
FileStream fs = File.OpenRead(path);
List<Province> listofProvinces = reader.ReadTextString(fs);
foreach (Province province in listofProvinces)
{
Provinces.Add(new Province()
{
Aristocrats = province.Aristocrats,
Artisans = province.Artisans
});
}
}
}
}
}
Code Behind
using isnuaatest.Helper;
using isnuaatest.Models;
using isnuaatest.ViewModel;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace isnuaatest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindowViewModel _vm = new MainWindowViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
private void OpenSaveFile_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog fileDialog = new OpenFileDialog();
fileDialog.Multiselect = false;
dynamic result = fileDialog.ShowDialog();
if (result == true)
{
_vm.SavegamePath = fileDialog.FileName;
}
}
}
}
My thinking is that maybe the Data Context wont update, because the data is in the Observable Collection. If this is true how can i update the Data Context, i already tried adding it in xaml to no avail.
Thanks
You actually create 3 different MainWindowViewModel objects - one in xaml and two in code behind. You can get rid of one in xaml, once in MainWindow constructor you set DataContext xaml-one is overridden.
But two objects in code-behind cause your problem - you load file into _vm object, but it's not the one that is held in DataContext.
To fix your problem use _vm for DataContext and not the new object:
public MainWindowViewModel _vm = new MainWindowViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _vm;
}
Change your Provinces:
public ObservableCollection<Province> Provinces
{
get { return this._province; }
set
{
_province = value;
OnPropertyChanged("Provinces");
}
}
I'm binding two comboboxes to the same listviewcollection. The problem is that selecting a value in one combobox, causes the other combobox selected item to change to the exact value of the first combobox. They are coupled and I want them to be independent of each other.
MyListViewCollection is like this
modelsView = new ListCollectionView(MainVM.All_Models);
All_Models is an Observable collection of custom objects like this
public ObservableCollection<MLModel> All_Models { get; set; } = new ObservableCollection<MLModel>() { };
I bind two ComboBoxes to modelsview like this
<ComboBox ItemsSource="{Binding Path=modelsView}" SelectedItem="{Binding SelectedModel_Right}" SelectionChanged="RightSideModelSelection">
<ComboBox ItemsSource="{Binding Path=modelsView}" SelectedItem="{Binding SelectedModel_Left}" SelectionChanged="LeftSideModelSelection">
So everything works fine, the comboboxes contain the identical lists of items from the models view which is what I want.
I definitely have bound the selected item to two separate properties in the view model, and those are
private MLModel _selectedModel_left;
public MLModel SelectedModel_Left
{
get { return _selectedModel_left; }
set
{
SetProperty(ref _selectedModel_left, value);
}
}
private MLModel _selectedModel_right;
public MLModel SelectedModel_Right
{
get { return _selectedModel_right; }
set
{
SetProperty(ref _selectedModel_right, value);
}
}
The only thing I can think of is that they are both referencing the same object in the collection, but I'm not exactly sure how it causes this behavior.
Also the desired behavior is that each combobox be able to select and display a different item from the same collection.
Any explanation would be helpful as well as the recommended way to decouple the two comboboxes selection from each other.
EDIT: As requested here is the code to create a minimal working example
MainWindow.xaml
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication3"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Vertical">
<ComboBox ItemsSource="{Binding Path=modelsView}" SelectedItem="{Binding SelectedModel_Left}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox ItemsSource="{Binding Path=modelsView}" SelectedItem="{Binding SelectedModel_Right}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
}
MLModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfApplication3
{
public class MLModel
{
public string Name { get; set; }
public string Type { get; set; }
}
}
ViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace WpfApplication3
{
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ViewModel()
{
modelsView = new ListCollectionView(All_Models);
//modelsView.Filter = modelsFilter;
}
public ListCollectionView modelsView { get; set; }
public ObservableCollection<MLModel> All_Models { get; set; } = new ObservableCollection<MLModel>() {
new MLModel() { Name = "One", Type = "TypeOne" },
new MLModel() {Name = "Two", Type = "TypeTwo" },
new MLModel() {Name = "Three", Type = "TypeThree" }
};
private MLModel _selectedModel_left;
public MLModel SelectedModel_Left
{
get { return _selectedModel_left; }
set
{
this._selectedModel_left = value;
NotifyPropertyChanged();
}
}
private MLModel _selectedModel_right;
public MLModel SelectedModel_Right
{
get { return _selectedModel_right; }
set
{
this._selectedModel_right = value;
NotifyPropertyChanged();
}
}
}
}
From the documentation:
If the target is an ItemsControl, the current item is synchronized with the selected item.
You are sharing the same ListCollectionView between both ComboBox objects. This means that the current selected item is updated in the view, and replicated in any other ItemsControl where that same view is used.
If you don't want this behavior, you need to give each ComboBox its own ListCollectionView object to bind to.
Alternatively, you can set the IsSynchronizedWithCurrentItem property of each ComboBox to false.
Hi i made a bindable combobox with MVVM and when i'm trying to get the value of combobox it gets the path of the value ex:I select a name and it return WpfApp1.Parts .
How can i get the name from combobox as string?
And if enyone know how can i save the combobox that when i add a new value , like when i enter again on the program my last entered value to be there!
View.Parts:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
namespace WpfApp1
{
public class Parts : Changed
{
public string name;
public string Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
RaisePropertyChanged("Name");
}
}
}
}
}
ViewModel.AddViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfApp1
{
public class AddViewModel : Changed
{
private ObservableCollection<Parts> _persons;
public string names;
public AddViewModel()
{
Persons = new ObservableCollection<Parts>()
{
new Parts{Name="Nirav"}
,new Parts{Name="Kapil"}
,new Parts{Name="Arvind"}
,new Parts{Name="Rajan"}
};
}
public ObservableCollection<Parts> Persons
{
get { return _persons; }
set {
if (_persons != value)
{
_persons = value;
RaisePropertyChanged("Persons");
}
}
}
private Parts _sperson;
public Parts SPerson
{
get { return _sperson; }
set {
if (_sperson != value)
{
_sperson = value;
RaisePropertyChanged("SPerson");
}
}
}
}
}
MainWindow:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public AddViewModel addviewmodel;
public MainWindow()
{
InitializeComponent();
addviewmodel = new AddViewModel();
DataContext = addviewmodel;
}
public AddViewModel getModel()
{
return addviewmodel;
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//textshow.Text = holo.SelectedItem;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
getModel().Persons.Add(new Parts { Name = cmbtxt.Text});
}
}
}
MainWindowXaml:
<Grid>
<ComboBox x:Name="holo" ItemsSource="{Binding Persons}" SelectedItem="{Binding SPerson}" SelectionChanged="ComboBox_SelectionChanged" HorizontalAlignment="Left" Margin="391,17,0,0" VerticalAlignment="Top" Width="314" Height="27">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox Name="cmbtxt" HorizontalAlignment="Left" Height="23" Margin="24,21,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="172" />
<Button Content="Add" HorizontalAlignment="Left" Margin="24,88,0,0" VerticalAlignment="Top" Width="156" Height="49" Click="Button_Click"/>
<TextBlock x:Name="textshow" HorizontalAlignment="Left" Text="{Binding Path=SPerson}" TextWrapping="Wrap" VerticalAlignment="Top" Margin="391,104,0,0" Height="33" Width="223"/>
</Grid>
You should bind to the Name property of the selected item returned by the SPerson property:
<TextBlock x:Name="textshow" HorizontalAlignment="Left"
Text="{Binding Path=SPerson.Name}"
TextWrapping="Wrap" VerticalAlignment="Top"
Margin="391,104,0,0" Height="33" Width="223"/>
What you currently see is the ToString() representation of the Parts class so the other option would to override this method:
public class Parts : Changed
{
public string name;
public string Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
RaisePropertyChanged("Name");
}
}
}
public override string ToString()
{
return name;
}
}
The error is here
<TextBlock x:Name="textshow" HorizontalAlignment="Left" Text="{Binding Path=SPerson}"
You cannot bind Text property to complex Object like Parts you should bind it to the Name as you did with ComboBox
I have one big problem :(. I create separate (XAML) control which I use as ListBoxItem and I want binding to this Listbox, list of my class object. If I don't have separate ListBoxItem it's not a problem to bind this data but I must do it like that.
Below this post I upload simple version of code which show you what exacty I want make.
UserControl1 (CS)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Id", typeof(string),
typeof(UserControl1), new FrameworkPropertyMetadata(string.Empty));
public string Id
{
get { return GetValue(TextProperty).ToString(); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty1 =
DependencyProperty.Register("name1", typeof(string),
typeof(UserControl1), new FrameworkPropertyMetadata(string.Empty));
public string name1
{
get { return GetValue(TextProperty1).ToString(); }
set { SetValue(TextProperty1, value); }
}
public static readonly DependencyProperty TextProperty2 =
DependencyProperty.Register("Surname", typeof(string),
typeof(UserControl1), new FrameworkPropertyMetadata(null));
public string Surname
{
get { return GetValue(TextProperty2).ToString(); }
set { SetValue(TextProperty2, value); }
}
public static readonly DependencyProperty TextProperty3 =
DependencyProperty.Register("Age", typeof(string),
typeof(UserControl1), new FrameworkPropertyMetadata(null));
public string Age
{
get { return GetValue(TextProperty3).ToString(); }
set { SetValue(TextProperty3, value); }
}
}
}
Main Window XAML
<ListBox x:Name="listBox1" ItemsSource="{Binding}" HorizontalAlignment="Left" Height="222" Margin="144,571,0,0" VerticalAlignment="Top" Width="404">
<ListBox.ItemTemplate>
<DataTemplate>
<local:UserControl1 FontStretch="Medium" Id="{Binding Path=id}" name="{Binding Path=Name1}" Age="{Binding Path=age}" Surname="blabla"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Main Window CS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication2
{
public partial class MainWindow : Window
{
List<zdarzenia> zdarz_list = new List<zdarzenia>();
public MainWindow()
{
InitializeComponent();
}
public class person
{
public string id;
public string name;
public string surname;
public string age;
public person(string Id, string Name, string Surname, string Age)
{
id = Id;
name = Name;
surname = Surname;
age = Age;
}
}
public List<person> list = new List<person>();
private void button_Click(object sender, RoutedEventArgs e)
{
list.Add(new person("1", "Ana", "Sophia", "35"));
list.Add(new person("2", "Jack", "Black", "40"));
listBox1.ItemsSource = list;
}
}
}
UserControl1.XAML is in the link I can't upload this part of code
Sorry I have some problems with certificate in a place where I work and I can't paste code properly. Here is better version if you want:
http://ideone.com/bm5XSP
http://ideone.com/HRTLlB
I will be grateful for your help
Thanks in advance
Pager.xaml(View)
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Client.View.Pager">
...
<ListBox x:Name="listBoxEntries"
ItemsSource="{Binding Path=ListCollectionView}"
BorderThickness="0"
Margin="0"
Style="{StaticResource common}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
ItemTemplate="{StaticResource templateTableCategory}"
SelectedItem="{Binding Path=SelectedEntry, Mode=TwoWay}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="{Binding Path=Rows}"
Columns="{Binding Path=Columns}"
IsItemsHost="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
...
</Grid>
Pager.xaml.cs(Behind code)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Client.Model;
using Client.ViewModel;
namespace Client.View
{
public partial class Pager
{
public static readonly DependencyProperty SelectedEntryProperty = DependencyProperty.Register("SelectedEntry", typeof(IPagableEntry), typeof(Pager));
...
public IPagableEntry SelectedEntry
{
get { return (DataContext as PagerViewModel).SelectedEntry; }
set { (DataContext as PagerViewModel).SelectedEntry = value; }
}
...
}
}
PagerViewModel.cs
namespace Client.ViewModel
{
public class PagerViewModel : ViewModelBase
{
...
IPagableEntry _selectedEntry;
public IPagableEntry SelectedEntry
{
get
{
return _selectedEntry;
}
set
{
_selectedEntry = value;
OnPropertyChanged("SelectedEntry");
}
}
...
}
}
MainPage.xaml(View)
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Client.View.MainPage"
Style="{StaticResource common}">
<Page.DataContext>
<viewModel:MainPageViewModel/>
</Page.DataContext>
...
<view:Pager x:Name="pagerTableCategories"
Grid.Row="0"
List="{Binding Path=PagerTableCategoriesItems}"
Rows="{Binding Path=PagerTableCategoriesRows}"
Columns="{Binding Path=PagerTableCategoriesColumns}"
SelectedEntry="{Binding Path=SelectedTableCategory, Mode=TwoWay}">
</view:Pager>
...
</Page>
MainPageViewModel.cs
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using Client.Model;
namespace Client.ViewModel
{
public class MainPageViewModel : ViewModelBase
{
...
IPagableEntry _selectedTableCategory;
public IPagableEntry SelectedTableCategory
{
get
{
return _selectedTableCategory;
}
set
{
_selectedTableCategory = value;
MessageBox.Show("Got it!");
}
}
...
}
}
I made a custom panel 'Pager' and that's ViewModel.
and I wanna show the pager in my MainPage.
I expect that I select an item, then MainPageViewModel's property SelectTableCategory will change, and an message box show up with string "Got it!"
But it doesn't works...
what's my problem?
ps. I am not very good at English.
I would appreciate your understanding.
Let's work from first principles.
You have a custom control called Pager, which is to be used like this
<view:Pager x:Name="pagerTableCategories"
SelectedEntry="{Binding Path=SelectedTableCategory, Mode=TwoWay}">
</view:Pager>
See how SelectedEntry is assigned a binding? This tells us that Pager must implement SelectedEntry as a dependency property. How do we do this?
public class Pager
{
// Using a DependencyProperty as the backing store for PrePend.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedEntryProperty =
DependencyProperty.Register("SelectedEntry", typeof(object),
typeof(Pager),
new PropertyMetadata(null, OnSelectedEntryChanged));
public object SelectedEntry
{
get { return GetValue(SelectedEntryProperty); }
set { SetValue(SelectedEntryProperty, value); }
}
private static void OnSelectedEntryChanged(DependencyObject pager, DependencyPropertyChangedEventArgs e)
{
// TODO set the SelectedItem in the ListBox
}
}
Next you need to display the Pager. Your XAML looks ok. You should add an event handler on the ListBox to listen to selection changed event. This way, you can update SelectedEntry when that occurs.
public class Pager
{
// continued...
// *Updated*
private listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.SelectedEntry = (sender as ListBox).SelectedItem;
}
}