I can't get custom control's selected item - c#

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;
}
}

Related

WPF ComboBox selection causes selection in second ComboBox

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.

Get value from a Combobox Binding MVVM not get correctly

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

Add to Combobox after binding MVVM WPF

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?

why does the datacontext in my WPF project function correctly from code behind but not from xaml? [duplicate]

This question already has answers here:
WPF Binding Image Source
(2 answers)
Closed 5 years ago.
I have been working on a small sample Wpf Mvvm project for experimenting with INotifyPropertyChanged interface. The project actually works correctly, but the problem that I am having is that the project only works correctly if I set the DataContext in the code behind of MainWindow.xaml. If I try to set the DataContext in the xaml markup then some of the features of the project don't work. The UI contains a textblock, textbox (for entering text to display in the textblock OnPropertyChanged) and submit button (which really does nothing except provide a place to lose focus from textbox) and 3 other buttons (color buttons) for changing the background color of the UI. The default color of the UI is orange -- until the color is changed by clicking any of the color buttons
There are 3 viewModels, PersonViewModel (which the textbox binds to), BackgroundViewModel (for the color buttons) and a MainViewModel which combines the two other viewModels. The viewModels reside in the viewModels folder of the project. There is also an ObservableObject class (ViewModelBase class basically) which implement INotifyPropertyChanged interface and gets inherited by PersonViewModel and BackgroundViewModel. ObservableObject.cs resides in the root folder of the project.
The Project isn't pure Mvvm. The color buttons use a click event in the code behind of MainWindow.xaml. If I set the DataContext in the Code behind of MainWindow.xaml everything works correctly. If I set the DataContext in the xaml markup -- the textbox/textblock features works but the color buttons won't change the background color of the UI. When I step through the code it runs through all the code correctly but the UI background colors don't change. I am guessing it is a binding thing.
The sample project can be downloaded here
The code is below. How can I make this project function correctly if I set the DataContext in the xaml markup? I tried the following binding on the Grid which WILL set the default orange color for the UI, but the color buttons don't work:
<Grid Background="{Binding Background.Color}" DataContext="{StaticResource bc}">
--MainWindow.xaml
<Window x:Class="NotifyChangeExample.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:NotifyChangeExample"
xmlns:VM="clr-namespace:NotifyChangeExample.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="550" Width="525">
<!--<Window.DataContext>
<VM:MainViewModel />
</Window.DataContext>-->
<Window.Resources>
<VM:MainViewModel x:Key="bc" />
</Window.Resources>
<Grid Background="{Binding Background.Color}" DataContext="{StaticResource bc}">
<!--<Grid Background="{Binding Background.Color}">-->
<DockPanel LastChildFill="False" Margin="0,82,0,0">
<StackPanel Width="150" DockPanel.Dock="Top">
<TextBlock Text="{Binding Person.Name, StringFormat=Welcome (0)}" />
<TextBox Text="{Binding Person.Name, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />
<Button>Submit</Button>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" DockPanel.Dock="Bottom" >
<Button Click="Red_Clicked">Red Background</Button>
<Button Click="Blue_Clicked">Blue Background</Button>
<Button Click="Yellow_Clicked">Yellow Background</Button>
</StackPanel>
</DockPanel>
</Grid>
</Window>
--MainWindow.xaml.cs
using NotifyChangeExample.ViewModels;
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 NotifyChangeExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
MainViewModel _main = new MainViewModel();
public MainWindow()
{
InitializeComponent();
//DataContext = _main;
}
private void Red_Clicked(object sender, RoutedEventArgs e)
{
_main.SetBackground(Brushes.Red);
}
private void Blue_Clicked(object sender, RoutedEventArgs e)
{
_main.SetBackground(Brushes.Blue);
}
private void Yellow_Clicked(object sender, RoutedEventArgs e)
{
_main.SetBackground(Brushes.Yellow);
}
}
}
--ObservableObject.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NotifyChangeExample
{
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
}
--PersonViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NotifyChangeExample.ViewModels
{
public class PersonViewModel : ObservableObject
{
private string _name;
public string Name
{
get
{
if (string.IsNullOrEmpty(_name))
return "Unknown";
return _name;
}
set
{
_name = value;
OnPropertyChanged("Name");
}
}
}
}
--BackgroundViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace NotifyChangeExample.ViewModels
{
public class BackgroundViewModel : ObservableObject
{
private Brush _color;
public Brush Color
{
get
{
if (_color == null)
return Brushes.Orange;
return _color;
}
set
{
_color = value;
OnPropertyChanged("Color");
}
}
}
}
--MainViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace NotifyChangeExample.ViewModels
{
public class MainViewModel
{
public PersonViewModel Person { get; private set; }
public BackgroundViewModel Background { get; private set; }
public MainViewModel()
{
Person = new PersonViewModel();
Background = new BackgroundViewModel();
}
public void SetBackground(Brush brushColor)
{
Background.Color = brushColor;
}
}
}
Your code behind is using the _main object so if you want to set the DataContext in the XAML, you just need to set _main using the DataContext.
So in the XAML you would have
<Window.DataContext>
<VM:MainViewModel />
</Window.DataContext>
and in your code behind you would set _main by casting the DataContext to a MainViewModel
MainViewModel _main;
public MainWindow()
{
InitializeComponent();
_main = (MainViewModel) DataContext;
}
Alternatively, remove the DataContext from XAML, and use this MainWindow constructor:
private readonly MainViewModel _main = new MainViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _main;
}
When you are binding your ViewModel from XAML it can't work, because in your code-behind you are setting the colors to your local ViewModel "_main". But _main is not bound to the View, bc is.

WPF C# ComboBox with multiple lines and accepting returns?

Is there an easy way to do a multiline combobox in WPF C#? What do I mean by this? I mean a textbox that supports multiple lines and carriage returns; where it word wraps... It needs to have a scrollbar so that if the text in the box is taller than the height of the combobox, the user can scroll down.
In addition, because its a combobox, it needs to have a dropdown button so that the user can quickly swap between groups of text. I've tried googling for this, but I can't find anyone talking about such a combobox.
EDITED FOR COMPLETE WORKING SOLUTION:
XAML:
<Window x:Class="delete.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ComboBox Height="30" Width="300" ItemsSource="{Binding items}" SelectedItem="{Binding item}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBox AcceptsReturn="True" TextWrapping="Wrap" Width="250" Height="30" Text="{Binding Name}" VerticalScrollBarVisibility="Auto"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Window>
Code-behind:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
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 delete
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
Setup();
this.DataContext = this;
}
ObservableCollection<Thang> _items;
public ObservableCollection<Thang> items
{
get { return _items; }
set
{
_items = value;
OnPropertyChanged("items");
}
}
private Thang _item;
public Thang item
{
get { return _item; }
set
{
_item = value;
OnPropertyChanged("item");
}
}
public void Setup()
{
items = new ObservableCollection<Thang>();
items.Add(new Thang("1", "One"));
items.Add(new Thang("2", "Two"));
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public class Thang
{
public Thang(string id, string name)
{
Name = name;
ID = id;
}
public string Name { get; set; }
public string ID { get; set; }
}
}

Categories

Resources