In my Code does my Function OnPropertyChanged not update the UI, despite the Fact that it does work everywhere else in my Programm. I try to upgrade the Content of the Button, so that if the User presses it, it will change it's Content. It does Update the value but then won't show it in the UI for some weird Reason. Any help is appreciated...
The View:
public partial class CreateMP : Window
{
public CreateMP()
{
InitializeComponent();
}
public CreateMP(string UserName)
{
CreateMPViewModel.User = UserName;
InitializeComponent();
}
}
The ViewModel:
public class CreateMPViewModel : Window
{
private string _ButtonContent = "Create Server";
private bool _ButtonEnable = true;
public event PropertyChangedEventHandler PropertyChanged;
private readonly TcpListener listener;
private TcpClient client;
public ICommand ButtonCommand_Back { get; set; }
public ICommand ButtonCommand_CreateGame { get; set; }
public string ButtonContent
{
get
{
return _ButtonContent;
}
set
{
if (value != _ButtonContent)
{
_ButtonContent = value;
OnPropertyChanged("ButtonContent");
}
}
}
public bool ButtonEnable
{
get
{
return _ButtonEnable;
}
set
{
if (value != _ButtonEnable)
{
_ButtonEnable = value;
OnPropertyChanged("ButtonEnable");
}
}
}
public static string User { get; set; }
public string IPAdresse { get; set; }
public int Passwort { get; set; }
public CreateMPViewModel()
{
ButtonCommand_Back = new DelegateCommand(BackButtonClick);
ButtonCommand_CreateGame = new DelegateCommand(CreateGameClick);
int Port = 51246;
Random rnd = new Random();
Password = rnd.Next(0, 99999);
IPAddress localAdd = IPAddress.Parse(IPAdresse);
listener = new TcpListener(localAdd, Port);
listener.Start();
}
private void BackButtonClick()
{
client = GameHandler.CurrentClient;
Application.Current.MainWindow.Close();
if (client != null)
{
client.Close();
}
if (listener != null)
{
listener.Stop();
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void CreateGameClick()
{
ButtonEnable = false;
ButtonContent = "Waiting for Player...";
//More Code here after the Update of the Button
}
}
The XAML:
<Window x:Class="CreateMP"
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"
mc:Ignorable="d"
Title="Game" Height="450" Width="400"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize" WindowStyle="None">
<Window.DataContext>
<local1:CreateMPViewModel/>
</Window.DataContext>
<Grid>
<Label Name ="CreateGame" Content="Create Game" Margin="109,46,118,322" FontSize="26" Foreground="#0074BC"/>
<Button Name ="btnCreateGame" Content="{Binding ButtonContent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="35" Margin="118,267,118,0" VerticalAlignment="Top" Background="#0074BC" Foreground="White" Command="{Binding ButtonCommand_CreateGame}" IsEnabled="{Binding ButtonEnable, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Name ="btnBack" Content="Back" Height="35" Margin="118,387,118,0" VerticalAlignment="Top" Background="#0074BC" Foreground="White" Command="{Binding ButtonCommand_Back}"/>
<Label Content="IP4-Adress:" HorizontalAlignment="Left" Height="30" Margin="92,138,0,0" VerticalAlignment="Top" Width="90" Foreground="#0074BC"/>
<Label Content="Password:" HorizontalAlignment="Left" Height="30" Margin="92,168,0,0" VerticalAlignment="Top" Width="90" Foreground="#0074BC"/>
<Label Content="{Binding IPAdresse}" HorizontalAlignment="Left" Height="30" Margin="187,138,0,0" VerticalAlignment="Top" Width="127" Foreground="#0074BC"/>
<Label Content="{Binding Password}" HorizontalAlignment="Left" Height="30" Margin="187,168,0,0" VerticalAlignment="Top" Width="127" Foreground="#0074BC"/>
</Grid>
</Window>
The CreateMPViewModel class must inherit from INotifyPropertyChanged.
public class CreateMPViewModel : Window, INotifyPropertyChanged
{
//........................................
//........................................
}
Related
Problem
I want to refresh my wpf view when a change is made in a List of objects in my application, but it wont register the INotifyChanged method when I change a value.
What I've tried
I went to multiple different stackoverflow pages with sort of the same problem but I don't get it working right. It wont register a change in a object in the list.
my code
below is the code for the MainWindow of the WPF application in wher with the last button click I change the value of XLocation in an object out of a list.
public partial class MainWindow : Window
{
private string filePathArtist { get; set; }
private string filePathGrid { get; set; }
public Game.Game Game { get; set; }
public MainWindow()
{
InitializeComponent();
filePathGrid = String.Empty;
filePathArtist = String.Empty;
}
private void BtnOpen_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
bool? res = openFileDialog.ShowDialog();
if (res == true)
{
string filepathgrid = openFileDialog.FileName;
filePathGrid = filepathgrid;
GridTextBox.Text = filepathgrid;
}
}
private void PickArtistBtn_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
bool? res = openFileDialog.ShowDialog();
if (res == true)
{
string filepathartist = openFileDialog.FileName;
filePathArtist = filepathartist;
ArtistTextBox.Text = filepathartist;
}
}
private void CreateGridBtn_Click(object sender, RoutedEventArgs e)
{
Game = new Game.Game(filePathGrid, filePathArtist);
this.DataContext = Game;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Game.Artists[0].XLocation = 30;
}
}
Next code is the Game class where is implemented a INotfyPropertyChanged on the list of Artists.
public class Game : INotifyPropertyChanged
{
public List<Artist> _artists;
public List<Artist> Artists
{
get
{
return _artists;
}
set
{
_artists = value;
OnPropertyChanged("Artists");
}
}
public List<ITile> Tiles { get; set; }
public Game()
{
}
public Game(string graphPath, string artistPath)
{
IDataParser graphParser = DataFactory.DataFactory.Instance.CreateParser(graphPath);
IDataParser artistParser = DataFactory.DataFactory.Instance.CreateParser(artistPath);
Tiles = graphParser.ParseGridData(graphPath);
Artists = artistParser.ParseArtistData(artistPath);
Test = "new Game";
}
public string Test { get; set; } = "t";
public event PropertyChangedEventHandler? PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Ive also added the INotifyPropertyChanged in the Artists class
public class Artist : INotifyPropertyChanged
{
private float _xLocation;
private float _yLocation;
private int _xVelocity;
private int _yVelocity;
public float XLocation
{
get => _xLocation;
set
{
_xLocation = value;
OnPropertyChanged("XLocation");
}
}
public float ConvertedXLoc
{
get => XLocation * (float)3.75;
set { }
}
public float YLocation
{
get => _yLocation;
set
{
_yLocation = value;
OnPropertyChanged("YLocation");
}
}
public float ConvertedYLoc
{
get => YLocation * (float)3.75;
set { }
}
public int XVelocity
{
get => _xVelocity;
set
{
_xVelocity = value;
}
}
public int YVelocity
{
get => _yVelocity;
set
{
_yVelocity = value;
}
}
public event PropertyChangedEventHandler? PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then here is the Xaml code where I bind the objects to the wpf UI.
<Window x:Class="BroadwayBoogie.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:game="clr-namespace:BroadwayBoogie.Game"
mc:Ignorable="d"
Title="MainWindow" Height="900" Width="900"
>
<Window.DataContext>
<game:Game/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="201*"/>
<ColumnDefinition Width="199*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="21*"/>
<RowDefinition Height="401*"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<Button x:Name="BtnOpen" Content="Pick Grid" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Click="BtnOpen_Click"/>
<TextBox x:Name="GridTextBox" HorizontalAlignment="Center" TextWrapping="NoWrap" VerticalAlignment="Center" Width="266" />
<Button x:Name="PickArtistBtn" Content="Pick Artist" HorizontalAlignment="Left" Margin="356,0,0,0" VerticalAlignment="Center" Click="PickArtistBtn_Click" RenderTransformOrigin="-0.135,0.647"/>
<TextBox x:Name="ArtistTextBox" HorizontalAlignment="Left" Margin="30,14,0,0" TextWrapping="NoWrap" VerticalAlignment="Top" Width="231" Grid.Column="1"/>
<Button x:Name="CreateGridBtn" Grid.Column="1" Content="Create Grid" HorizontalAlignment="Left" Margin="311,14,0,0" VerticalAlignment="Top" Click="CreateGridBtn_Click"/>
<Canvas Width="800" Height="800" Grid.ColumnSpan="2" Margin="49,15,51,27" Grid.Row="1" Background="DarkSeaGreen" Grid.RowSpan="2">
<ItemsControl Name="tilesItemsControl" ItemsSource="{Binding Tiles}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<Rectangle
Width="15"
Height="15"
Fill="{Binding Color}"
Canvas.Left ="{Binding ConvertedXLoc}"
Canvas.Top="{Binding ConvertedYLoc}" />
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Name="ArtistItemsControl" ItemsSource="{Binding Artists}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<Rectangle
Width="3.75"
Height="3.75"
Fill="Black"
Canvas.Left ="{Binding ConvertedXLoc}"
Canvas.Top="{Binding ConvertedYLoc}" />
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
<Grid/>
<Button Content="Button" HorizontalAlignment="Left" Margin="4,0,0,0" Grid.Row="2" VerticalAlignment="Center" Click="Button_Click"/>
</Grid>
So with the press of the button I added for testing purposes. It changes a value in the List and then the PropertyChanged method should detect that but it doesn't detect it it just skips it.
Question
So my basic question is, how do I detect the change of a property from objects out of the List of Artists.
OnPropertyChanged will only be executed when the property itself is changed. A new item in a list is not a property change. That's the reason why no updates happens.
Instead a List, try an ObservableCollection. An ObservableCollection implements an additional INotifyCollectionChanged which makes the UI able to react on changing items in the list.
When I click on a row of WPF datagrid, I want to open a new Window that has information about a person from the clicked row (that can be changed) by using binding. How can I do it? And how can I save the changed information?
Thanks in Advance!
I did solve a simelar propblem using the Behaviours Libary.
First Make sure you have the Nuget package: "Microsoft.Xaml.Behaviors.Wpf" installed.
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
<DataGrid>
<i:Interaction.Triggers>
<i:EventTrigger
EventName="MouseUp">
<i:InvokeCommandAction
Command="{Binding OpenWindowCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}, Path=SelectedItem}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
Hope this snippet helps.
You can add a SelectedItem property in the ViewModel. Bind that to DataGrid's SelectedItem. Now you know which item the user has selected (clicked on).
In the DataGrid's MouseDown or MouseUp event you can open the new Window with the same ViewModel object as the DataContext. That way the new Window knows which item was selected. Bind the new Window's fields to the SelectedItem in the ViewModel.
If you have set up the INotifyPropertyChanged correctly, the values that are changed in the new Window also will show up in the DataGrid in the first Window.
Since the SelectedItem also is part of the collection, you will automatically have the changed values in the collection as well. (I assume this is what you mean with "save the changed information").
if your collection, that is bound to the DataGrid, is a collection of Person objects, the "SelectedItem property" could look like this:
public Person SelectedPerson
{
get
{
return _selectedPerson;
}
set
{
_selectedPerson = value;
PropertyChanged....
}
}
private Person _selectedPerson;
ViewModel.cs
namespace Procect1
{
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
ListOfPeople = new ObservableCollection<Person>();
}
string image;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private void Window2OpenExecute()
{
Window2 Window2 = new Window2(this);
Window2.Show();
}
private void AddPhotoExecute()
{
foreach (Window window in Application.Current.Windows)
{
if (window.GetType() == typeof(Window2))
{
OpenFileDialog op = new OpenFileDialog();
op.Title = "Select a picture";
op.Filter = "All supported graphics|*.jpg;*.jpeg;*.png|" +
"JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
"Portable Network Graphic (*.png)|*.png";
if (op.ShowDialog() == true)
{
(window as Window2).imgPhoto.Source = new BitmapImage(new Uri(op.FileName));
image = op.FileName;
}
}
}
}
private void AddPersonToListExecute()
{
foreach (Window window in Application.Current.Windows)
{
if (window.GetType() == typeof(Window2))
{
ListOfPeople.Add(new Person()
{
Name = (window as Window2).textBox_FirstName.Text,
Surname = (window as Window2).textBox_LastName.Text,
IdNumber = (window as Window2).textBox_IdNumber.Text,
Age = (window as Window2).textBox_Age.Text,
Image = image
});
(window as Window2).Close();
}
}
}
private void SerializeExecute()
{
Stream stream = File.OpenWrite(Environment.CurrentDirectory + "\\Serialization1.xml");
XmlSerializer people = new XmlSerializer(typeof(ObservableCollection<Person>));
people.Serialize(stream, ListOfPeople);
stream.Close();
}
private void DeSerializeExecute()
{
XmlSerializer deserializer = new XmlSerializer(typeof(ObservableCollection<Person>));
TextReader textReader = new StreamReader(Environment.CurrentDirectory + "\\Serialization1.xml");
ListOfPeople = (ObservableCollection<Person>)deserializer.Deserialize(textReader);
textReader.Close();
}
public ICommand Window2Open { get { return new RelayCommand(Window2OpenExecute); } }
public ICommand AddPersonToList { get { return new RelayCommand(AddPersonToListExecute); } }
public ICommand Serialize { get { return new RelayCommand(SerializeExecute); } }
public ICommand DeSerialize { get { return new RelayCommand(DeSerializeExecute); } }
public ICommand AddPhoto { get { return new RelayCommand(AddPhotoExecute); } }
public ObservableCollection<Person> ListOfPeople
{
get
{
return listOfPeople;
}
set
{
listOfPeople = value;
OnPropertyChanged("ListOfPeople");
}
}
public Person SelectedPerson
{
get
{
return _selectedPerson;
}
set
{
_selectedPerson = value;
OnPropertyChanged(nameof(SelectedPerson));
}
}
private Person _selectedPerson;
private ObservableCollection<Person> listOfPeople;
public event PropertyChangedEventHandler PropertyChanged;
}
}
Person.cs
namespace Procect1
{
public class Person : INotifyPropertyChanged
{
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged(nameof(Name));
}
}
public string Surname
{
get
{
return surname;
}
set
{
surname = value;
OnPropertyChanged(nameof(Surname));
}
}
public string IdNumber
{
get
{
return idNumber;
}
set
{
idNumber = value;
OnPropertyChanged(nameof(IdNumber));
}
}
public string Age
{
get
{
return age;
}
set
{
age = value;
OnPropertyChanged(nameof(Age));
}
}
public string Image
{
get
{
return image;
}
set
{
image = value;
OnPropertyChanged(nameof(Image));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
private string name;
private string surname;
private string idNumber;
private string age;
private string image;
}
}
MainWindow.xaml
<Window x:Class="Procect1.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:Procect1"
mc:Ignorable="d"
Title="Lista pacjentów" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<!--<RowDefinition Height="auto" />-->
<!--<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>-->
</Grid.RowDefinitions>
<DataGrid Name="listView" MouseUp="listView_MouseUp" ItemsSource="{Binding ListOfPeople}" AutoGenerateColumns="False" SelectedItem="{Binding Index}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Header="Imię" />
<DataGridTextColumn Binding="{Binding Surname}" Header="Nazwisko" />
<DataGridTextColumn Binding="{Binding IdNumber}" Header="Pesel" />
<DataGridTextColumn Binding="{Binding Age}" Header="Wiek" />
<DataGridTemplateColumn Header="Zdjęcie" Width=" 100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Image}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Canvas>
<Button Canvas.Right="20" Canvas.Top="30" Height="50" Width="80" FontSize="18" Command="{Binding Window2Open}">Dodaj</Button>
<Button Canvas.Right="20" Canvas.Top="100" Height="50" Width="80" FontSize="18" Command="{Binding Serialize}">Serializuj</Button>
<Button Canvas.Right="20" Canvas.Top="170" Height="50" Width="80" FontSize="18" Command="{Binding DeSerialize}">Załaduj</Button>
</Canvas>
</Grid>
</Window>
MainWindow.cs
public partial class MainWindow : Window
{
public MainWindow( )
{
InitializeComponent();
}
private void listView_MouseUp(object sender, MouseButtonEventArgs e)
{
Window3 window3 = new Window3();
window3.Show();
}
}
Window2.xaml
<Window x:Class="Procect1.Window2"
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:Procect1"
mc:Ignorable="d"
Title="Dane Pacjenta" Height="600" Width="500">
<Canvas>
<Label Canvas.Left="20" Canvas.Top="20" FontSize="24">Imię</Label>
<TextBox x:Name="textBox_FirstName" Canvas.Left="190" Canvas.Top="30" Height="28" Width="200" ></TextBox>
<Label Canvas.Left="20" Canvas.Top="100" FontSize="24">Nazwisko</Label>
<TextBox x:Name="textBox_LastName" Canvas.Left="190" Canvas.Top="110" Height="28" Width="200"></TextBox>
<Label Canvas.Left="20" Canvas.Top="180" FontSize="24">Pesel</Label>
<TextBox x:Name="textBox_IdNumber" Canvas.Left="190" Canvas.Top="190" Height="28" Width="200"></TextBox>
<Label Canvas.Left="20" Canvas.Top="260" FontSize="24">Wiek</Label>
<TextBox x:Name="textBox_Age" Canvas.Left="190" Canvas.Top="270" Height="28" Width="200"></TextBox>
<Label Canvas.Left="20" Canvas.Top="340" FontSize="24">Zdjęcie</Label>
<Image Name="imgPhoto" Canvas.Left="190" Canvas.Top="350" Height="120" Width="150"></Image>
<Button Canvas.Right="20" Canvas.Top="400" FontSize="16" Command="{Binding AddPhoto}" >Dodaj zdjęcie</Button>
<Button Canvas.Left="250" Canvas.Bottom="20" Height="40" Width="80" FontSize="24" Command="{Binding AddPersonToList}">Zapisz</Button>
</Canvas>
</Window>
MainWindow2.cs
public partial class Window2 : Window
{
public Window2(ViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}
Window3.xaml
<Window x:Class="Procect1.Window3"
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:Procect1"
mc:Ignorable="d"
Title="Dane Pacjenta" Height="600" Width="500">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Canvas>
<Label Canvas.Left="20" Canvas.Top="20" FontSize="24">Imię</Label>
<TextBox x:Name="textBox_FirstName" Text="{Binding SelectedPerson.Name}" Canvas.Left="190" Canvas.Top="30" Height="28" Width="200" ></TextBox>
<Label Canvas.Left="20" Canvas.Top="100" FontSize="24">Nazwisko</Label>
<TextBox x:Name="textBox_LastName" Text="{Binding SelectedPerson.Surname}" Canvas.Left="190" Canvas.Top="110" Height="28" Width="200"></TextBox>
<Label Canvas.Left="20" Canvas.Top="180" FontSize="24">Pesel</Label>
<TextBox x:Name="textBox_IdNumber" Text="{Binding SelectedPerson.IdNumber}" Canvas.Left="190" Canvas.Top="190" Height="28" Width="200"></TextBox>
<Label Canvas.Left="20" Canvas.Top="260" FontSize="24">Wiek</Label>
<TextBox x:Name="textBox_Age" Text="{Binding SelectedPerson.Age}" Canvas.Left="190" Canvas.Top="270" Height="28" Width="200"></TextBox>
<Label Canvas.Left="20" Canvas.Top="340" FontSize="24">Zdjęcie</Label>
<Image Name="imgPhoto" Source="{Binding SelectedPerson.Image}" Canvas.Left="190" Canvas.Top="350" Height="120" Width="150"></Image>
<Button Canvas.Right="20" Canvas.Top="400" FontSize="16" Command="{Binding AddPhoto}" >Dodaj zdjęcie</Button>
<Button Canvas.Left="250" Canvas.Bottom="20" Height="40" Width="80" FontSize="24">Zapisz</Button>
</Canvas>
</Window>
In Window3.cs I haven't added any code.
RylayCommand.cs
public class RelayCommand : ICommand
{
private readonly Func<bool> _canExecute;
private readonly Action _execute;
public RelayCommand(Action execute)
: this(execute, null)
{
}
public RelayCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public void Execute(object parameter)
{
_execute();
}
}
I post it as an answer because I couldn't add it to the question.
On MainWindow you have bound the SelectedItem to "Item". If you look at the Output window (if you are using Visual Studio) when you are running the program, you find following error:
System.Windows.Data Error: 40 : BindingExpression path error: 'Index' property not found on 'object' ''ViewModel'
XAML is a very quite beast, so look there first if the bindings doesn't work.
You should bind the SelectedItem to the SelectedPerson in the ViewModel.
SelectedItem="{Binding SelectedPerson}"
To get the window3 to work, you need to set the DataContext to the main windows DataContext (the view model).
Window3 window3 = new Window3();
window3.DataContext = this.DataContext;
window3.Show();
I'm new to the WPF MVVM. I want to know about how to detect SelectedCellsChanged event inside my ViewModel. Is there any way to detect that event without putting any code into the code behind file. This is my code.
MainWindow.xaml
<Window x:Class="WpfApplication1.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"
DataContext="{StaticResource CusVM}">
<Grid>
<Button x:Name="myButton" Command="{Binding MyButtonClickCommand}" Width="100" Height="50" Content="click" Margin="0,10,417,260" />
<Label Content="{Binding Name}" Margin="105,37,23,251" />
<TextBox x:Name="inputBox1" Width="200" Height="30" Margin="22,74,295,216" Text="{Binding Text1, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Width="200" Height="30" Margin="263,74,54,216" />
<ComboBox HorizontalAlignment="Left" Margin="122,10,0,0" VerticalAlignment="Top" Width="236" ItemsSource="{Binding Addresses}" SelectedItem="{Binding SelectedAddress}" DisplayMemberPath="AddressLine1" >
</ComboBox>
<DataGrid Margin="0,109,0,10" ItemsSource="{Binding Addresses}"/>
</Grid>
View Model : CustomerViewModel
namespace WpfApplication1.ViewModels
{
public class CustomerViewModel : EventBase
{
public ICommand MyButtonClickCommand
{
get { return new DelegateCommand(FuncToCall, FuncToEvaluate); }
}
private Address selected_address;
public Address SelectedAddress
{
get { return selected_address; }
set { selected_address = value; OnPropertyChanged("SelectedAddress"); Name = value.AddressLine1; }
}
IEnumerable<Address> addresses = new List<Address>();
public IEnumerable<Address> Addresses
{
get { return addresses; }
set
{
addresses = value;
OnPropertyChanged("Addresses");
}
}
public CustomerViewModel()
{
fillList();
}
private void fillList()
{
List<Address> addr = new List<Address>();
addr.Add(new Address() { AddressID=1, AddressLine1="test1"});
addr.Add(new Address() { AddressID=2, AddressLine1="test2"});
addr.Add(new Address() { AddressID = 3, AddressLine1 = "test3" });
addresses = addr;
}
private string text1;
public string Text1
{
get { return text1; }
set {
text1 = value;
OnPropertyChanged("Text1");
Name = text1;
}
}
private string name;
public string Name
{
get { return name; }
set {
name = value;
OnPropertyChanged("Name");
}
}
private void FuncToCall(object context)
{
Name = "test result";
}
private bool FuncToEvaluate(object context)
{
return true;
}
}
}
I think you might find the answer here. I would've add it as a comment, but I don't have enough rep just yet.
First: I am new to MVVM and WPF.
I am trying to create a little application with a tabbed user interface. Users can create products and storage locations, using a button which should open a new TabItem.
My code in the view looks like this:
<TabControl ItemsSource="{Binding Workspaces}"
IsSynchronizedWithCurrentItem="True"
Margin="3"
DockPanel.Dock="Top">
<TabControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
and the View Model is this:
ObservableCollection<WorkspaceViewModel> _workspaces;
public ObservableCollection<WorkspaceViewModel> Workspaces
{
get
{
if (_workspaces == null)
{
_workspaces = new ObservableCollection<WorkspaceViewModel>();
}
return _workspaces;
}
set
{
_workspaces = value;
}
}
public void AddProduct(object obj)
{
Workspaces.Add(new ProductViewModel());
}
Various other buttons add different ViewModels to the Workspaces Collection.
I have defined multiple Data Template (one for each ViewModel). Here is one:
<DataTemplate DataType="{x:Type vm:ProductViewModel}">
<vw:ProductView />
</DataTemplate>
The WorkspaceViewModel is this:
namespace Inventory.Desktop.ViewModels
{
public abstract class WorkspaceViewModel : INotifyPropertyChanged
{
#region Events and EventHandlers
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
and eg the ProductViewModel
namespace Inventory.Desktop.ViewModels
{
public class ProductViewModel: WorkspaceViewModel
{
private Product _product;
private string _displayName;
public string DisplayName
{
get
{
if (String.IsNullOrEmpty(_displayName))
{
return "Neues Produkt";
} else
{
return _displayName;
}
}
set
{
_displayName = value;
NotifyPropertyChanged("DisplayName");
}
}
#region Public Properties
public Product Product
{
get
{
return _product;
}
set
{
_product = value;
NotifyPropertyChanged("Product");
}
}
public string Title
{
get
{
return _product.Title;
}
set
{
_product.Title = value;
NotifyPropertyChanged("Title");
}
}
public string ScanCode
{
get
{
return _product.ScanCode;
}
set
{
_product.ScanCode = value;
NotifyPropertyChanged("ScanCode");
}
}
public string Manufacturer
{
get
{
return _product.Manufacturer;
}
set
{
_product.Manufacturer = value;
NotifyPropertyChanged("Manufacturer");
}
}
public string ManufacturerNumber
{
get
{
return _product.ManufacturerNumber;
}
set
{
_product.ManufacturerNumber = value;
NotifyPropertyChanged("ManufacturerNumber");
}
}
public string Description
{
get
{
return _product.Description;
}
set
{
_product.Description = value;
NotifyPropertyChanged("Description");
}
}
#endregion
#region Commands
private ICommand _saveCommand;
public ICommand SaveCommand
{
get
{
return _saveCommand;
}
set
{
_saveCommand = value;
}
}
#endregion
#region Command Executions
public void Save(object obj)
{
using (var db = new InvContext())
{
db.Products.Attach(Product);
db.Entry(Product).State = Product.ProductId == 0 ?
EntityState.Added : EntityState.Modified;
db.SaveChanges();
}
MessageBox.Show("Product saved: " + Product.Title);
}
#endregion
#region Constructors
public ProductViewModel()
{
if (_product == null)
{
_product = new Product();
}
SaveCommand = new RelayCommand(new Action<object>(Save));
}
#endregion
}
}
Here the ProductView.xaml view:
<UserControl x:Class="Inventory.Desktop.Views.ProductView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="450">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" FlowDirection="RightToLeft">
<Button Name="SaveProductButton" Command="{Binding SaveCommand}" Content="Speichern" Margin="3" BorderThickness="0">
</Button>
</StackPanel>
<StackPanel DockPanel.Dock="Top" VerticalAlignment="Stretch">
<Label Content="Scan Code" />
<TextBox Text="{Binding Path=ScanCode}" HorizontalAlignment="Stretch" Margin="3" Padding="3" Height="50" TextAlignment="Right">
<TextBox.Background>
<ImageBrush ImageSource="..\Images\Barcode32.png" AlignmentX="Left" Stretch="None" />
</TextBox.Background>
</TextBox>
<Label Content="Bezeichnung" />
<TextBox Text="{Binding Path=Title, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="3" />
<Label Content="Hersteller" />
<TextBox Text="{Binding Path=Manufacturer, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="3" />
<Label Content="Hersteller Nummer" />
<TextBox Text="{Binding Path=ManufacturerNumber, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="3" />
<Label Content="Beschreibung / Information" />
<TextBox Text="{Binding Path=Description, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="3" />
</StackPanel>
</DockPanel>
</UserControl>
and here the code-behind ProductView.xaml.cs:
namespace Inventory.Desktop.Views
{
/// <summary>
/// Interaktionslogik für ProductView.xaml
/// </summary>
public partial class ProductView : UserControl
{
ProductViewModel _productModel = new ProductViewModel();
public ProductView()
{
InitializeComponent();
base.DataContext = _productModel;
}
}
}
What's currently working:
When I click a button, I got a new TabItem displaying the correct view and all commands work correctly.
What's not working:
When I open a TabItem, enter some information, and then I open another TabItem with a different ViewModel, switching the focus to the new TabItem and then back to the original oen, then all entered information are gone (object is null).
When I open a TabItem, enter some information, and then I open another TabItem with the same ViewModel, then both TabItems show the the same information.
When I add a new TabItem, it doesn't get focus.
I am totally lost and I hope you can tell me what I am doing wrong.
Best
Stefan
Have a property on your ViewModel to store the reference to current/selected tab
public WorkspaceViewModel SelectedTab
{
get { return _selectedTab; }
set
{
_selectedTab = value;
RaisePropertyChanged(() => SelectedTab);
}
}
and bind this to SelectedItem property on TabControl.
<TabControl ItemsSource="{Binding Workspaces}"
SelectedItem="{Binding SelectedTab, Mode=TwoWay}"
Margin="3"
DockPanel.Dock="Top">
<TabControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
And finally, you want to update SelectedTab property whenever you are adding a new tab. Modify your AddProduct like this:
public void AddProduct(object obj)
{
var workspace = new ProductViewModel();
Workspaces.Add(workspace);
SelectedTab = workspace;
}
I have a class News
public class News : ObservableCollection<New>
{
public News()
: base()
{
}
}
A class New
public class New : INotifyPropertyChanged
{
public PhotoAttachments Photo
{
get
{
return photoAttachments;
}
set
{
photoAttachments = value;
OnPropertyChanged("Photo");
}
}
// some fields such as date, text, id, sourceName etc
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string info)
{//realisation of method}
public PhotoAttachments photoAttachments = new PhotoAttachments(); // it is a collection, such as News, but it contains objects of class PhotoAttachment, which have property with string url to photo
}
after
InitializeComponent(); i write this.listBox.ItemsSource = NewsList;
so a have a listbox with objects of class New.
In these listbox I created another one listbox, and tried to fill it by PhotoAttachments collection. And here I have a problem, listbox with photos don't show photos(but they exists). Here is XAML:
// I can select different <local:NewsTemplateSelector.Photos>
//style of listbox <DataTemplate>
//using NewsTemplateSelector <Border BorderBrush="Red" BorderThickness="2" Width="400" Height="300" Margin="10">
<StackPanel Orientation="Horizontal" Width="400" Height="300">
<Image Source="{Binding SourceImage}" Height="75" Width="75" Margin="0,-225,0,0" />
<Canvas Width="400">
<TextBlock Text="{Binding SourceName}" Foreground="Black" FontSize="25" TextWrapping="Wrap" Height="65" Width="326" d:LayoutOverrides="VerticalAlignment, Height" />
<ListBox Name="photoListbox" ItemsSource="{Binding Photo}" Height="229" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="326" Canvas.Top="69">
<Image Source="{Binding Big}" Height="200" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="400" />
</ListBox>
</Canvas>
</StackPanel>
</Border>
</DataTemplate>
</local:NewsTemplateSelector.Photos>
PhotoAttachment class:
public class PhotoAttachment : INotifyPropertyChanged
{
private string ownerId;
public string OwnerId { get { return ownerId; } set { ownerId = value; OnPropertyChanged("OwnerId"); } }
private string small;
public string Small { get { return small; } set { small = value; OnPropertyChanged("Small"); } }
private string big;
public string Big { get { return big; } set { big = value; OnPropertyChanged("Big"); } }
public PhotoAttachment(string ownId, string small, string big)
{
ownerId = ownId;
this.small = small;
this.big = big;
}
public PhotoAttachment() { }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
Just realized that your XAML for photoListView is missing an ItemTemplate
Something along these lines should do the trick:
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Big}" Height="200" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="400" />
</DataTemplate>
</ListBox.ItemTemplate>