My application has a grid with two columns. In the left column, I have a ListView, with some items with data binding. On the Right I want to implement a View that will display content based on which item
is selected on the ListView. How do I do that?
<Window x:Class="WpfApplication8.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" Background="LightBlue">
<Grid Name="MainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<ListView Grid.Column="0" Name="listView" ItemsSource="{Binding Path=ClientCollection}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ClientName}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
I believe the best way to achieve that is to add a selectedItem property to you ViewModel, witch is binded to the selected item of your ListView, and make that property the dataContext of the Right View;
here is a an example :
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView Grid.Column="0" x:Name="LvListView" ItemsSource="{Binding ClientCollection}" SelectedItem="{Binding SelectedClient}" >
<ListView.View>
<GridView>
<GridViewColumn Width="150" Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Width="150" Header="Age" DisplayMemberBinding="{Binding Age}"/>
<GridViewColumn Width="150" Header="Location" DisplayMemberBinding="{Binding Location}"/>
</GridView>
</ListView.View>
</ListView>
<Grid Grid.Column="1" VerticalAlignment="Center" DataContext="{Binding SelectedClient,Mode=TwoWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Name: " Grid.Column="0" Grid.Row="0"/>
<TextBlock Text="Age: " Grid.Column="0" Grid.Row="1"/>
<TextBlock Text="Location: " Grid.Column="0" Grid.Row="2"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Name}"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Age}"/>
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Location}"/>
</Grid>
</Grid>
and in the coresponding MainWindow.cs :
public partial class MainWindow : Window, INotifyPropertyChanged
{
private Client _selectedClient = new Client();
public Client SelectedClient
{
get { return _selectedClient; }
set
{
if (_selectedClient == value) return;
_selectedClient = value;
OnPropertyChanged();
}
}
private ObservableCollection<Client> _clientCollection = new ObservableCollection<Client>();
public ObservableCollection<Client> ClientCollection
{
get { return _clientCollection; }
set
{
if (_clientCollection == value) return;
_clientCollection = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
ClientCollection = new ObservableCollection<Client>()
{
new Client()
{
Name = "James",Age = 34, Location = "Paris"
},
new Client()
{
Name = "Joe",Age = 34, Location = "Us"
},
new Client()
{
Name = "Ann",Age = 34, Location = "Canada"
},
};
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Client
{
public string Name { get; set; }
public string Location { get; set; }
public int Age { get; set; }
}
and don't forget to set the datacontext for the mainWindow View, in that case
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Related
I am trying to create a "notes" app using wpf mvvm. I have a MainWindow containing a DataGrid with data that is bound to an ObservableCollection. In MainWindowView I have a "Find" button which calls FindWindowDialog. In the FindWindowDialog in the textbox, I must enter the text that will be searched and click "Find", after which the DataGrid MainWindowView should hide those lines whose content does not contain the searched text. I don't really know how to do this, after 2 days of searching I decided to ask a question. I googled on this topic and I have a suggestion that I should delve into the messenger pattern and converters
MainWindow.xaml(View)
<Window x:Class="NotesARK6.View.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:NotesARK6.View"
xmlns:model="clr-namespace:NotesARK6.Model"
xmlns:viewmodel="clr-namespace:NotesARK6.ViewModel"
mc:Ignorable="d"
Title="Notes" Height="450" Width="800"
x:Name="_mainWindow"
WindowStartupLocation="CenterScreen">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding NotesCollection}" SelectedItem="{Binding SelectedNote}" IsReadOnly="True" AutoGenerateColumns="False" x:Name="DataGrid_Notes" Margin="5" Grid.Row="2" Grid.Column="1">
<DataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding EditNoteCommand}" CommandParameter="{Binding SelectedNote}" />
</DataGrid.InputBindings>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Width="1*" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Content" Width="3*" Binding="{Binding Content}"/>
</DataGrid.Columns>
</DataGrid>
<ToolBar Grid.Row="1" Grid.Column="1" Margin="5">
<Button Content="Create" Command="{Binding CreateNewNoteCommand}"/>
<Separator />
<Button Content="Delete" Command="{Binding DeleteNoteCommand}" CommandParameter="{Binding SelectedNote}"/>
<Separator />
<Button Content="Find" Command="{Binding FindNoteCommand}"/>
</ToolBar>
</Grid>
</Window>
FindWindowDialog.xaml(view)
<Window x:Class="NotesARK6.ViewModel.Dialogs.FindWindowDialog"
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:NotesARK6.ViewModel.Dialogs"
mc:Ignorable="d"
Title="Find" Height="250" Width="400"
WindowStartupLocation="CenterScreen"
Topmost="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="35"/>
<RowDefinition Height="60"/>
<RowDefinition Height="50"/>
<RowDefinition Height="90"/>
</Grid.RowDefinitions>
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox IsChecked="{Binding SearchByName}" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5" Content="Search by name" Grid.Column="0"></CheckBox>
<CheckBox IsChecked="{Binding SearchByContent}" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5" Content="Search by content" Grid.Column="1"></CheckBox>
</Grid>
<TextBox Text="{Binding SearchString}" Margin="5" Grid.Row="2" Grid.Column="1"/>
<Button Command="{Binding FindNotesCommand}" Margin="5" Content="Find" Grid.Row="3" Grid.Column="1" />
</Grid>
</Window>
FindWindowDialogViewModel.cs
public class FindWindowDialogViewModel : INotifyPropertyChanged
{
private string searchString;
private bool searchByName;
private bool searchByContent;
//Controll Commands
public ControllComands FindNotesCommand { get; private set; }
//Controll Commands
public FindWindowDialogViewModel()
{
FindNotesCommand = new ControllComands(FindNote);
}
public string SearchString
{
get
{
return searchString;
}
set
{
searchString = value;
OnPropertyChanged();
}
}
public bool SearchByName
{
get
{
return searchByName;
}
set
{
searchByName = value;
OnPropertyChanged("SearchByName");
}
}
public bool SearchByContent
{
get
{
return searchByContent;
}
set
{
searchByContent = value;
OnPropertyChanged("SearchByContent");
}
}
public void FindNote()
{
MessageBox.Show(SearchByName.ToString() + " " + SearchByContent.ToString());
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
How can I use the command contained in FindWindowDialogViewModel to hide rows in the DataGrid MainWindowView ?
I would like something like this: (this is pseudocode)
public void FindNote()
{
foreach(var row in MainWindow.DataGrid.Rows)
{
string searchingText = FindNoteDialog.TextBox.Text;
if (!row.Content.Contains(searchingText))
{
row.Visibillity = false;
}
}
}
For this purpose collections provide filtering via their views (see Binding to collections and CollectionView API remarks to learn more).
Filtering using the collection's view has much better performance than hiding rows or removing items from the original collection.
To do so, you must get the ICollectionView of the source collection
Note.cs
class Note
{
public string Summary { get; set; }
public DateTime Timestamp { get; set; }
}
ViewModel.cs
class ViewModel : INotifyPropertyChanged
{
public ObservableCollection<Note> Notes { get; }
public string SearchKey { get; set; }
public ICommand FilterNotesCommand => new RelayCommand(ExecuteFilterNotes);
private void ExecuteFilterCommands(object commandParameter)
{
ICollectionView notesView = CollectionViewSource.GetDefaultView(this.Notes);
notesView.Filter = item => (item as Note).Summary.Contains(this.SearchKey);
}
}
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding SearchKey}" />
<Button Command="{Binding FilterNotesCommand}" Content="Filter Table" />
<DataGrid ItemsSource="{Binding Notes}" />
</StackPanel>
</Window>
i'm trying to code a POS system. i want to have two list, one with articles and when user click on an article the article is added to the second list and the list reflesh automatically.
here is my code
using Newtonsoft.Json;
public class ArticlesDetailsViewModel : INotifyPropertyChanged, IArticleViewModel
{
public ArticlesDetailsViewModel()
{
string baseUrl = "http://api.market.csa.com/api/v1.1/Article/ListArticle";
bool dr;
using (HttpClient client = new HttpClient())
{
var response = client.GetAsync(baseUrl);
var result = response.Result;
dr = result.IsSuccessStatusCode;
if (dr)
{
string jsonstring = result.Content.ReadAsStringAsync().Result;
var articles = JsonConvert.DeserializeObject<ListArticleResponse>(jsonstring);
foreach(var art in articles.data)
{
ListArticle.Add(art);
}
}
else
{
MessageBox.Show("Une erreur s'est produite lors de la recuration des articles, vérifiez votre connexion internet", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
//intializing ICollectionView using collection(ObservableCollection)
ArticleCollection = CollectionViewSource.GetDefaultView(ListArticle);
CommandeCollection = CollectionViewSource.GetDefaultView(ListCommande);
ArticleCollection.Filter = Filter;
}
private ObservableCollection<ArticleModel> _ListArticle = new ObservableCollection<ArticleModel>();
public ObservableCollection<ArticleModel> ListArticle
{
get { return _ListArticle; }
set { _ListArticle= value;
NotifyPropertyChanged("ListArticle");
}
}
private ICollectionView _ArticleCollection;
public ICollectionView ArticleCollection
{
get { return _ArticleCollection; }
set { _ArticleCollection= value;
NotifyPropertyChanged("ArticleCollection");
}
}
private ICollectionView _CommandeCollection;
public ICollectionView CommandeCollection
{
get { return _CommandeCollection; }
set { _CommandeCollection=value;
NotifyPropertyChanged("CommandeCollection");
}
}
private bool Filter(object emp)
{
ArticleModel employee = emp as ArticleModel;
//you can write logic for filter here
if (!string.IsNullOrEmpty(ArticleName) && !string.IsNullOrEmpty(ArticleCode))
return employee.NomArticle.Contains(ArticleName) && employee.NomArticle.Contains(ArticleName);
else if (string.IsNullOrEmpty(ArticleName))
return employee.Code.Contains(ArticleCode);
else
return employee.NomArticle.Contains(ArticleName);
}
private string _articleName = string.Empty;
public string ArticleName
{
get { return _articleName;
}
set
{
_articleName=value;
NotifyPropertyChanged("ArticleName");
ArticleCollection.Refresh();
}
}
private string _articleCode = string.Empty;
public string ArticleCode
{
get { return _articleCode; }
set
{
_articleCode= value;
NotifyPropertyChanged("ArticleCode");
ArticleCollection.Refresh();
}
}
private ArticleModel _ArticleSelected = new ArticleModel();
public ArticleModel ArticleSelected
{
get {
return _ArticleSelected; }
set
{
_ArticleSelected=value;
NotifyPropertyChanged("ArticleSelected");
}
}
private ObservableCollection<ArticleModel> _ListCommande = new ObservableCollection<ArticleModel>();
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
PropertyChanged(this, new PropertyChangedEventArgs("CommandeCollection"));
}
}
public ObservableCollection<ArticleModel> ListCommande
{
get
{
return _ListCommande;
}
set
{
_ListCommande= value;
NotifyPropertyChanged("ListCommande");
}
}
}
}
the xaml for the commande list:
<UserControl
x:Class="WantaMarketDesktop.View.CommandesListingView"
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:local="clr-namespace:WantaMarketDesktop.View"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="0.5*"/>
</Grid.ColumnDefinitions>
<ListView ScrollViewer.CanContentScroll="True" ScrollViewer.IsDeferredScrollingEnabled="True" ScrollViewer.VerticalScrollBarVisibility="Visible" ItemsSource="{Binding CommandeCollection,UpdateSourceTrigger=PropertyChanged}" Grid.ColumnSpan="2" >
<ListView.View>
<GridView AllowsColumnReorder="True" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.CanContentScroll="True" >
<GridView.Columns>
<GridViewColumn Header=" ID" DisplayMemberBinding="{Binding IdArticle}"/>
<GridViewColumn Header="Nom" DisplayMemberBinding="{Binding NomArticle}"/>
<GridViewColumn Header="Quantite" DisplayMemberBinding="{Binding Quantite}"/>
<GridViewColumn Header="Prix" DisplayMemberBinding="{Binding Prix}"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<Label Grid.Row="1" Content="Montant total de la commande : " FontSize="13"/>
<Label Grid.Row="1" Grid.Column="1" FontWeight="SemiBold" Content="2750 XFA" FontSize="17" Width="auto" VerticalAlignment="Center" HorizontalAlignment="Stretch" FontStyle="Normal" />
</Grid>
</UserControl>
the xaml article list
<UserControl x:Class="WantaMarketDesktop.View.ArticlesDetails"
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"
xmlns:local="clr-namespace:WantaMarketDesktop.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="0,10,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="5">
<TextBlock Text="Code : " VerticalAlignment="Center" Margin="10,5,5,5"/>
<TextBox Text="{Binding ArticleCode,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Width="200"/>
<TextBlock Text="Nom : " VerticalAlignment="Center" Margin="10,5,5,5"/>
<TextBox Text="{Binding ArticleName,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Width="200"/>
</StackPanel>
<StackPanel Grid.Row="1" Margin="10,0">
<Separator HorizontalAlignment="Stretch" Height="5" Margin="0,5,0,10"/>
<!--setting ItemsSource of ListView to ICollectionView-->
<ListView Cursor="Hand" ItemsSource="{Binding ArticleCollection}" SelectedItem="{Binding ArticleSelected}" HorizontalAlignment="Stretch" VerticalAlignment="Top" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Image ToolTip="{Binding Carracteristique}" Source="https://phonesdata.com/files/models/Xiaomi-Redmi-Note-10S-298.jpg" HorizontalAlignment="Stretch" VerticalAlignment="Top" Stretch="UniformToFill" MinHeight="50" MaxHeight="100" />
<TextBlock Text="{Binding NomArticle}" ToolTip="{Binding NomArticle}" HorizontalAlignment="Stretch" FontSize="12" FontFamily="Franklin Gothic Medium" VerticalAlignment="Bottom" />
<WrapPanel Orientation="Horizontal" Margin="0,5,0,0">
<TextBlock Text="{Binding Prix}" HorizontalAlignment="Right" FontSize="11" FontFamily="Franklin Gothic Medium" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding Code}" Margin="60,0,0,0" HorizontalAlignment="Stretch" FontSize="9" FontFamily="Franklin Gothic Medium" VerticalAlignment="Center" />
</WrapPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
</UserControl>
and the Interface
public interface IArticleViewModel
{
ObservableCollection<ArticleModel> ListArticle { get; set; }
ObservableCollection<ArticleModel> ListCommande { get; set; }
}
for now i have this
i want to do that when we click on article image , the article add to the list at the left
It is esssential that all controls that depend on the same data (DataContext) must share the same instance of the view model class (DataContext).
Normally you don't set the DataContext explicitly. You use DataContext inheritance where all child elements implicitly inherit their DataContext from their parent element.
Add ArticlesDetailsViewModel as a public property to your MainViewModel. Set the MainViewModel as the DataContext of the MainWindow. Then let the common parent of both UserControl elements bind its DataContext to the ArticlesDetailsViewModel property of the MainViewModel (current DataContext).
Don't forget to remove the DataContext assignment from all UserControl constructors.
MainViewModel.cs
class MainViewModel : INotifyPropertyChanged
{
// Shared instance
public IArticleViewModel ArticlesDetailsViewModel { get; }
public MainViewModel => this.ArticlesDetailsViewModel = new ArticlesDetailsViewModel();
}
MainWindow.xaml.cs
partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponenet();
this.DataContext = new MainViewModel();
}
}
MainWindow.xaml
<!-- DataContext is 'MainViewModel' ==> explcitly set -->
<Window>
<!-- DataContext is 'MainViewModel' ==> inherited -->
<StackPanel>
<!-- DataContext is 'MainViewModel' ==> inherited -->
<Button />
<!-- DataContext is 'MainViewModel' ==> inherited -->
<ListBox />
<!-- DataContext is 'ArticlesDetailsViewModel' ==> explcitly set -->
<StackPanel DataContext="{Binding ArticlesDetailsViewModel}">
<!-- DataContext is 'ArticlesDetailsViewModel' ==> inherited -->
<CommandesListingView />
<!-- DataContext is 'ArticlesDetailsViewModel' ==> inherited -->
<ArticlesDetails />
</StackPanel>
</StackPanel>
</Window>
I am still new in MVVM and WPF
and I've looked at some examples however still did not found exactly the answers to my question.
I have a ListBox which each Item should add a new user control using ObservableCollection. In the user control I have an several Text blocks which I want to bound the texts of them to the same ObservableCollection which contain the Data.
However I am not sure how to bind the Text blocks to the ObservableCollection
Would be happy for a code example.
I'm also attaching my code, this is my userControl XAML:
<UserControl x:Class="ProtocolAnalyzerGui.UserControlls.MenuControlls.UCSingleLine"
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="100" d:DesignWidth="300">
<Grid Background="#FF454545">
<Grid.RowDefinitions>
<RowDefinition Height="1*"></RowDefinition>
<RowDefinition Height="1*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock x:Name="TBHeader" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" ></TextBlock>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock x:Name="TBDatanTime" Grid.Column="0" Foreground="White" Text="{Binding DataAndTime }" ></TextBlock>
<TextBlock x:Name="TBComPort" Grid.Column="2" Foreground="White" Text="{Binding ComPort }" ></TextBlock>
<TextBlock x:Name="TBTranslation" Grid.Column="4" Foreground="White" Text="{Binding Translation }" ></TextBlock>
<TextBlock x:Name="TBDataBytesArray" Grid.Column="6" Foreground="White" Text="{Binding Header }" ></TextBlock>
</Grid>
</Grid>
</UserControl>
in the main window XAML:
<ScrollViewer Grid.Row="3" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ListBox x:Name="LBListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<myControlls:UCSingleLine x:Name="DataUserContoll"
DataContext="{Binding DataForGui}"></myControlls:UCSingleLine>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
the binding of the listbox Itemsource is inside this function:
private void MI_SerialPortStart_Click(object sender, RoutedEventArgs e)
{
LBListBox.ItemsSource = DataForGui;
_SerialPortTakeCare.Start();
}
also attaching my dataCode:
public class Data : INotifyPropertyChanged
{
private string _DataAndTime;
public string DataAndTime
{
get { return _DataAndTime; }
set
{
_DataAndTime = value;
OnPropertyChanged("DataAndTime");
}
}
private string _ComPort;
public string ComPort
{
get { return _ComPort; }
set
{
_ComPort = value;
OnPropertyChanged("ComPort");
}
}
private string _Translation;
public string Translation
{
get { return _Translation; }
set
{
_Translation = value;
OnPropertyChanged("Translation");
}
}
private string _Header;
public string Header
{
get { return _Header; }
set
{
_Header = value;
OnPropertyChanged("Header");
}
}
private string _Data_ARR;
public string Data_ARR
{
get { return _Data_ARR; }
set
{
_Data_ARR = value;
OnPropertyChanged("Data_ARR");
}
}
public Data()
{
_ComPort = "";
_Data_ARR = "";
_DataAndTime = "";
_Header = "";
_Translation = "";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You just need to set the data context of your user control and then add the appropriate bindings.
In your main window, this line should look as follows:
<local:UserControl1 DataContext="{Binding}" x:Name="DataUserContoll"/>
And in the user control, the code should look more like this:
<TextBlock x:Name="TBDatanTime" Grid.Column="0" Foreground="White" Text="{Binding TextField1}" />
<TextBlock x:Name="TBComPort" Grid.Column="2" Foreground="White" Text="{Binding TextField2}"/>
<TextBlock x:Name="TBTranslation" Grid.Column="4" Foreground="White" Text="{Binding TextField3}"/>
<TextBlock x:Name="TBDataBytesArray" Grid.Column="6" Foreground="White" Text="{Binding TextField4}"/>
... and you should change the placeholder field names I have put in (e.g. "TextField1") to be the actual string properties of the objects inside your collection "DataForGui".
I have the Contact class that implements the INotifyPropertyChanged:
public class Contact : INotifyPropertyChanged
{
public Contact(Contact contact)
{
this.Username = contact.Username;
this.GUID = contact.GUID;
this.Msg = contact.Msg;
this.Ring = contact.Ring;
}
private string username;
public string Username
{
get { return username; }
set
{
username = value;
NotifyPropertyChanged(nameof(Username));
}
}
public Guid GUID { get; set; }
public bool Msg { get; set; }
private bool ring;
public bool Ring
{
get { return ring; }
set
{
ring = value;
NotifyPropertyChanged(nameof(Ring));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
This is the main page:
public sealed partial class MainPage : Page
{
public ObservableCollection<Contact> Contacts = new ObservableCollection<Contact>();
public MainPage()
{
this.InitializeComponent();
Contacts.Add(new Contact("Contact001", Guid.NewGuid(), false, false));
Contacts.Add(new Contact("Contact002", Guid.NewGuid(), false, false));
}
private void AddContactButton_Click(object sender, RoutedEventArgs e)
{
Contacts.Add(new Contact("ContactN", Guid.NewGuid(), false, false));
}
private void ContactsListView_ItemClick(object sender, ItemClickEventArgs e)
{
Contact clickedContact = (Contact)e.ClickedItem;
int index = Contacts.IndexOf(clickedContact);
Contacts.ElementAt(index).Username = "Qwerty";
}
}
}
This is the XAML:
<Page
x:Class="ContactsListBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ContactsListBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:data="using:ContactsListBinding.Models"
xmlns:namespace="ContactsListBinding.Models">
<Page.Resources>
<data:MessageToImageConverter x:Key="MessageToImageConverter" />
<data:RingToImageConverter x:Key="RingToImageConverter" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0">
<Button Name="AddContactButton" Content="Add Contact" Click="AddContactButton_Click" />
<CheckBox Name="MessageMeCheckBox" Content="Message me" />
<CheckBox Name="DeleteMeCheckBox" Content="Delete me" />
</StackPanel>
<ListView Grid.Row="1" Grid.Column="0" Name="ContactsListView"
IsItemClickEnabled="True"
ItemClick="ContactsListView_ItemClick"
ItemsSource="{x:Bind Contacts}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Contact">
<Grid Width="500">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Bind Username}" VerticalAlignment="Center" />
<Image Grid.Row="0" Grid.Column="1" Width="15" Height="15" Source="{x:Bind Msg, Converter={StaticResource MessageToImageConverter}}" />
<Image Grid.Row="0" Grid.Column="2" Width="15" Height="15" Source="{x:Bind Ring, Converter={StaticResource RingToImageConverter}}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
So, when I click on an item, I change it's Ring property to true. I have debugged and it is changing to true. The only problem is that my UI isn't updating. Any ideas why?
Okay guys, I finally got it to work. It seems that the Binding Mode should be set to OneWay, instead of the default OneTime. For example the correct xaml should be:
<TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Bind Username, Mode=OneWay}" VerticalAlignment="Center" />
Thank you very much for all your help! :)
I don't know why but my Visibility Binding isn't working ONLY in the DataTemplate. Did I forget something?
Edit: All Bindings (except for this one work perfectly)
Thats the structure.
<Window>
<Grid>
<Grid>
<Grid Grid.Row="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ItemsControl x:Name="Targets" Margin="0,4,0,4">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,5,0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox TextWrapping="Wrap" Foreground="{Binding Foreground, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" Text="{Binding Address, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" Tag="{Binding}" PreviewKeyDown="ChangeLocationAddress" PreviewGotKeyboardFocus="TxtGotKeyboardFocusHandler" LostKeyboardFocus="ChangeLocationAddress" />
<Button Margin="2,0,0,0" Grid.Column="1" Content=" ↑ " Click="MoveLocationUp" Visibility="Visible" />
<Button Margin="2,0,0,0" Grid.Column="2" Content=" ↓ " Click="MoveLocationDown" Visibility="{Binding Path = UpDownButtonVisibility, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button x:Name="btnNewAddress" Grid.Row="1" Content="Neues Ziel hinzufügen" Margin="0,4,0,4" Visibility="{Binding Path=TargetButtonVisibility}"/>
</Grid>
</Grid></Grid></Grid></Window>
Codebehind:
public MapView(){
this.DataContext = this.ViewModel = new MapVM();
this.InitializeComponent();
this.Targest.Itemssource = this.ViewModel.ToLocations;
}
ViewModel:
public MapVM()
{ this.UpDownButtonVisibility = Visibility.Collapsed;
this.TargetButtonVisibility = Visibility.Collapsed;
}
private Visibility _UpDownButtonVisibility;
/// <summary>
/// Property Visibility für "↓" und "↑"
/// </summary>
public Visibility UpDownButtonVisibility
{
get { return _UpDownButtonVisibility; }
set
{
this._UpDownButtonVisibility = value;
NotifyPropertyChanged("UpDownButtonVisibility");
}
}
public Visibility TargetButtonVisibility { get; set; }
EDIT:
Program Output:
BindingExpression path error: 'UpDownButtonVisibility' property not found on 'object' ''Location' (HashCode=-794088449)'. BindingExpression:Path=UpDownButtonVisibility; DataItem='Location' (HashCode=-794088449); target element is 'Button' (Name=''); target property is 'Visibility' (type 'Visibility') 1.10s
Any suggestions?
I cannot find a PropertyChanged event handler and a call to it in your code. Add the INotifyPropertyChanged to your DataContext object and it should work
Personally I would model the visibility as a bool and use the BooleasnToVisibility Converter that comes with WPF.
Change string to Visibility
public Visibility UpDownButtonVisibility { get; set; }
this.UpDownButtonVisibility = Visibility.Collapsed;
Add INPC and binding to a View model.
Here is working sample:
XAML
<Window x:Class="ItemsControlDataTemplate.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:ItemsControlDataTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ItemsControl x:Name="Targets" Margin="0,4,0,4" ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,5,0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox TextWrapping="Wrap" Foreground="{Binding Foreground, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" Text="{Binding Address, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" Tag="{Binding}" />
<Button Margin="2,0,0,0" Grid.Column="1" Content=" ↑ " Visibility="Visible" />
<Button Margin="2,0,0,0" Grid.Column="2" Content=" ↓ " Visibility="{Binding Path=UpDownButtonVisibility}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
C#
class MainWindowViewModel : INotifyPropertyChanged
{
ObservableCollection<MapVM> _items = new ObservableCollection<MapVM>();
public ObservableCollection<MapVM> Items { get { return _items; } }
public MainWindowViewModel()
{
Items.Add(new MapVM() { UpDownButtonVisibility = Visibility.Visible, Address = "1111111" });
Items.Add(new MapVM() { UpDownButtonVisibility = Visibility.Collapsed, Address = "222222" });
Items.Add(new MapVM() { UpDownButtonVisibility = Visibility.Visible, Address = "33333333" });
}
public event PropertyChangedEventHandler PropertyChanged;
}
class MapVM : INotifyPropertyChanged
{
public MapVM()
{
this.UpDownButtonVisibility = Visibility.Collapsed;
this.TargetButtonVisibility = Visibility.Collapsed;
}
private Visibility _upDownButtonVisibility;
public Visibility UpDownButtonVisibility
{
get { return _upDownButtonVisibility; }
set
{
_upDownButtonVisibility = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UpDownButtonVisibility)));
}
}
private Visibility _targetButtonVisibility;
public Visibility TargetButtonVisibility
{
get { return _targetButtonVisibility; }
set
{
_targetButtonVisibility = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TargetButtonVisibility)));
}
}
private string _address;
public string Address
{
get { return _address; }
set
{
_address = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Address)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
The DataContext of your TargetButtonVisibility binding is your main MapVM. This works ok.
The DataContext within your DataTemplate is not MapVM, is it the item being displayed by the template.
Since you have not supplied any ItemsSource binding on your ItemsControl we have no way of knowing what this actually is.
Also, as unkreativ points out, do not use Visibility in your VM, since this is a view related type. Use bool instead and call the property IsUpDownButtonVisible or similar.
EDIT: Assuming you do actually want to bind to your single MapVM, you could use a RelativeSource to find the parent ItemsControl:
<Button Margin="2,0,0,0"
Grid.Column="2"
Content=" ↓ "
Click="MoveLocationDown"
Visibility="{Binding DataContext.UpDownButtonVisibility,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}"/>
However, since you've already named your ItemsControl (Targets), you can simply refer to it by name:
<Button Margin="2,0,0,0"
Grid.Column="2"
Content=" ↓ "
Click="MoveLocationDown"
Visibility="{Binding DataContext.UpDownButtonVisibility,
ElementName=Targets}"/>