I asked a question where I can't make my content appear inside my DataGrid and I thought then I try a working solution and work my way up from there. However when I stumbled accross this MSDN article and copy-pasted its XAML content right inside my freshly created Window (in my new, empty WPF app) the issue persists: no content appears in the DataGrid.
Here is my Window's XAML:
<Window x:Class="DataGridTry.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:DataGridTry"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<!--DataTemplate for Published Date column defined in Grid.Resources. PublishDate is a property on the ItemsSource of type DateTime -->
<DataTemplate x:Key="DateTemplate" >
<StackPanel Width="20" Height="30">
<Border Background="LightBlue" BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding PublishDate, StringFormat={}{0:MMM}}" FontSize="8" HorizontalAlignment="Center" />
</Border>
<Border Background="White" BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding PublishDate, StringFormat={}{0:yyyy}}" FontSize="8" FontWeight="Bold" HorizontalAlignment="Center" />
</Border>
</StackPanel>
</DataTemplate>
<!--DataTemplate for the Published Date column when in edit mode. -->
<DataTemplate x:Key="EditingDateTemplate">
<DatePicker SelectedDate="{Binding PublishDate}" />
</DataTemplate>
</Grid.Resources>
<DataGrid Name="DG1" ItemsSource="{Binding}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<!--Custom column that shows the published date-->
<DataGridTemplateColumn Header="Publish Date" CellTemplate="{StaticResource DateTemplate}" CellEditingTemplate="{StaticResource EditingDateTemplate}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
As you see, just copy-paste, but still doesn't give the expected result. Shall I reinstall VS? (using 2015 ATM). What could cause the issue?
You're not binding your DataGrid properly. You haven't posted your model, but as an example, try creating an ObservableCollection in the code-behind for your new window and the bind your DataGrid to that collection:
<Window x:Class="DataGridTry.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:DataGridTry"
mc:Ignorable="d" Name="Root"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<!--DataTemplate for Published Date column defined in Grid.Resources. PublishDate is a property on the ItemsSource of type DateTime -->
<DataTemplate x:Key="DateTemplate" >
<StackPanel Width="20" Height="30">
<Border Background="LightBlue" BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding PublishDate, StringFormat={}{0:MMM}}" FontSize="8" HorizontalAlignment="Center" />
</Border>
<Border Background="White" BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding PublishDate, StringFormat={}{0:yyyy}}" FontSize="8" FontWeight="Bold" HorizontalAlignment="Center" />
</Border>
</StackPanel>
</DataTemplate>
<!--DataTemplate for the Published Date column when in edit mode. -->
<DataTemplate x:Key="EditingDateTemplate">
<DatePicker SelectedDate="{Binding PublishDate}" />
</DataTemplate>
</Grid.Resources>
<DataGrid DataContext="{Binding ElementName=Root}" Name="DG1" ItemsSource="{Binding PublishedDateModels}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<!--Custom column that shows the published date-->
<DataGridTemplateColumn Header="Publish Date" CellTemplate="{StaticResource DateTemplate}" CellEditingTemplate="{StaticResource EditingDateTemplate}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
An example model:
public class PublishedDateModel : INotifyPropertyChanged
{
private DateTime _publishDate;
public DateTime PublishDate
{
get { return _publishDate; }
set
{
if (value.Equals(_publishDate)) return;
_publishDate = value;
OnPropertyChanged();
}
}
public PublishedDateModel(DateTime date)
{
PublishDate = date;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And in your MainWindow.cs file:
public ObservableCollection<PublishedDateModel> PublishedDateModels { get; set; }
public MainWindow()
{
PublishedDateModels = new ObservableCollection<PublishedDateModel>(new[]
{
new PublishedDateModel(DateTime.Now),
new PublishedDateModel(DateTime.Now.AddDays(1)),
new PublishedDateModel(DateTime.Now.AddDays(-1)),
});
InitializeComponent();
}
There's nothing wrong with the XAML. It is just incomplete.
Name="DG1" ItemsSource="{Binding}"
DataGrid DG1 needs data bound to it.
For a quick example, add this to the code-behind (MainWindow.xaml.cs):
using System.Collections.ObjectModel;
namespace DataGridTry
{
public class ThingBeingPublished
{
public DateTime PublishDate { get; set; }
};
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var listOfThings = new ObservableCollection<ThingBeingPublished();
listOfThings.Add(new ThingBeingPublished() { PublishDate = DateTime.Now.AddDays(-2) });
listOfThings.Add(new ThingBeingPublished() { PublishDate = DateTime.Now.AddDays(-1) });
listOfThings.Add(new ThingBeingPublished() { PublishDate = DateTime.Now });
DG1.ItemsSource = listOfThings;
}
}
}
Now you should see:
Ideally, this would not be done in the code-behind.
Related
I'm trying to create a sort of ShellView without using thirdparty frameworks, and I'm doing trying to do it via. ContentControls and UserControls.
I can navigate/switch the UserControls that is showed inside the ContentControls as long as the commands are fired from outside the UserControls, but nothing is happening when I move the code inside a button in a UserControl.
Currently I'm having one MainWindow.XAML, where I have one ContentControl.
This ContentControl is hosting either a LoginWindowUserControl, or a UserWindowUserControl - I want to be able switch the UserControl from within one of the UserControls.
My MainWindow.XAML looks like this:
<Window x:Class="ModelHealthApplication.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:vms="clr-namespace:ModelHealthApplication.ViewModels"
xmlns:ia="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:views="clr-namespace:ModelHealthApplication.Views.UserControls"
xmlns:local="clr-namespace:ModelHealthApplication"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type vms:CurrentWindowUserStateViewModel}">
<views:UserView />
</DataTemplate>
<DataTemplate DataType="{x:Type vms:LoginWindowViewModel}">
<views:LoginView />
</DataTemplate>
<vms:NavigationViewModel x:Key="nVm" />
</Window.Resources>
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding Source={StaticResource nVm}, Path=OpenLoginWindowCommand}" />
</ia:EventTrigger>
</ia:Interaction.Triggers>
<Grid DataContext="{StaticResource nVm}">
<DockPanel>
<Button Content="Test" DockPanel.Dock="Left" Command="{Binding OpenUserWindowStateCommand}" />
<ContentControl x:Name="WindowUserState" Content="{Binding CurrentWindowUserState}" />
</DockPanel>
</Grid>
And my UserWindowUserControl looks like this:
<UserControl x:Class="ModelHealthApplication.Views.UserControls.UserView"
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:vms="clr-namespace:ModelHealthApplication.ViewModels"
xmlns:views="clr-namespace:ModelHealthApplication.Views.UserControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ModelHealthApplication.Views.UserControls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate DataType="{x:Type vms:MyModelsViewModel}">
<views:MyModelsView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:MyAccountViewModel}">
<views:MyAccountView />
</DataTemplate>
<vms:NavigationViewModel x:Key="nVm" />
</UserControl.Resources>
<Grid>
<DockPanel DataContext="{StaticResource nVm}">
<Grid DockPanel.Dock="Top" Background="{StaticResource MainBlue}" Height="25">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Column="0">
<Button Content="My Models"
HorizontalAlignment="Center"
Command="{Binding OpenMyModelsCommand}"
Style="{StaticResource NavButtonStyle}"/>
<Button Content="My Account"
HorizontalAlignment="Center"
Command="{Binding OpenMyAccountCommand}"
Style="{StaticResource NavButtonStyle}"
/>
</StackPanel>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right" Grid.Column="1" Margin="0, 0, 10, 0">
<TextBlock VerticalAlignment="Center" Foreground="White">
<Run Text="Logged in as:" FontWeight="Bold"/>
<Run Text="{Binding LoggedInAs}" d:Text="TestUser" />
</TextBlock>
<TextBlock Margin="20, 0 ,0 ,0" Text="Log Out" VerticalAlignment="Center" TextDecorations="Underline" Foreground="{StaticResource ComplenetarySecondOrange}" Background="{DynamicResource MainBlue}" Cursor="Hand">
<TextBlock.InputBindings>
<MouseBinding Command="{Binding OpenLoginWindowCommand}" MouseAction="LeftClick" />
</TextBlock.InputBindings>
</TextBlock>
</StackPanel>
</Grid>
<ContentControl x:Name="Pages" Content="{Binding SelectedViewModel}" />
</DockPanel>
</Grid>
</UserControl>
When I press the "Log Out" button/textblock I want to navigate back to the login window, but I can only do this from buttons that exist outside the UserControl.
I've read several other post similar to this, but I haven't found a solution that fits my needs - I tried using RelativeSource but without success - maybe someone can see what I'm doing wrong.
I'm using a "NavigationViewModel" to hold the commands which as I mentioned, works fine outside the UserControls:
using ModelHealthApplication.Commands;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace ModelHealthApplication.ViewModels
{
public class NavigationViewModel : INotifyPropertyChanged
{
public ICommand OpenMyModelsCommand { get; set; }
public ICommand OpenMyAccountCommand { get; set; }
public ICommand OpenUserWindowStateCommand { get; set; }
public ICommand OpenLoginWindowCommand { get; set; }
private object currentWindowUserState;
public object CurrentWindowUserState
{
get { return currentWindowUserState; }
set
{
currentWindowUserState = value;
OnPropertyChanged("CurrentWindowUserState");
}
}
private object selectedViewModel;
public object SelectedViewModel
{
get { return selectedViewModel; }
set
{
selectedViewModel = value;
OnPropertyChanged("SelectedViewModel");
}
}
public NavigationViewModel()
{
OpenMyModelsCommand = new OpenMyModelsCommand(this);
OpenMyAccountCommand = new OpenAccountCommand(this);
OpenUserWindowStateCommand = new OpenUserWindowStateCommand(this);
OpenLoginWindowCommand = new OpenLoginWindowCommand(this);
}
public void OpenUserWindowState(object obj)
{
CurrentWindowUserState = new CurrentWindowUserStateViewModel();
}
public void OpenLoginWindow(object obj)
{
CurrentWindowUserState = new LoginWindowViewModel();
}
public void OpenMyModels(object obj)
{
SelectedViewModel = new MyModelsViewModel();
}
public void OpenMyAccount(object obj)
{
SelectedViewModel = new MyAccountViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
}
Ended up solving this with a great tutorial from SingletonSean on Youtube.
Posting here if anyone comes across this, and has the same issue.
Singleton Sean MVVM Navigatoin
I want to allow user to change the font size a listbox during runtime. What's the simplest way to achieve that? (also, is there a way to simplify the code in general?)
Here's a xaml for the list I have: https://pastebin.com/Y8q5W50S
<ListBox Grid.Row="1" Grid.Column="1" Name="CardsListBox" ItemsSource="{Binding Path=placement}"
Background="Transparent" BorderBrush="Transparent">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" Style="{StaticResource ExpanderStyle}">
<Expander.Header>
<TextBlock Text="{Binding Name}" Style="{StaticResource PlacementHeaderStyle}" />
</Expander.Header>
<ItemsPresenter IsEnabled="False" />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=power}" Style="{StaticResource CardPowerStyle}" />
<TextBlock Grid.Column="2" Text="{Binding Path=name}" Style="{StaticResource CardNameStyle}" />
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So here is an example: Size of the font inside the item template of the ListBox is binded from ViewModel. The same value is also binded to slider so you can check how it works and change it on runtime.
First, you will have to create the view model where you will have to keep the value of font size. The view model will have to implement INotify property changed.
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
Items = new ObservableCollection<string> {"item1", "item2"};
_fontSize = 12d;
}
public ObservableCollection<string> Items { get; set; }
private double _fontSize;
public double FontSize
{
get => _fontSize;
set
{
_fontSize = value;
OnPropertyChanged(nameof(FontSize));
}
}
#region INotifyPropertyChangedImplementation
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Next step is to set view model as view data context for example like this ( Please, checkout for example, Caliburn Micro or Prism to make this automatically).
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = new MainWindowViewModel();
InitializeComponent();
}
}
After that bind the font size to the list using a relative source binding from ListBox item template to window DataContext ( MainWindowViewModel).
Ex:
<Window x:Class="ListBindingWpfApp.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:ListBindingWpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel>
<TextBlock Text="Font size slider:"></TextBlock>
<Slider Maximum="30" Minimum="5" Value="{Binding FontSize}"/>
</StackPanel>
<ListBox Grid.Row="1" Name="CardsListBox" ItemsSource="{Binding Path=Items}"
Background="Transparent" BorderBrush="Transparent">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontSize="{Binding DataContext.FontSize, RelativeSource={RelativeSource AncestorType=Window}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I have attached working project.
Sorry for the not detailed description I will update it later.
Sample Project
In my application I have a window which contain a ListBox, and controls that should show different properties of its currently selected item. Those controls are:
TextBox that should show 'Name' property.
TextBox that should show 'DataFile` property.
DataGrid that should show 'TItems property, which is an ObservableCollection.
I tried to bind SelectedItem to an object, and then bind different properties of that object to the controls mentioned above, with no success.
The window:
My View:
<Window
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:ReportMaker"
xmlns:ViewModel="clr-namespace:ReportMaker.ViewModel" x:Class="ReportMaker.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<ViewModel:MainViewModel/>
</Window.DataContext>
<Grid>
<Button x:Name="button" Content="Create" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="75"/>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="120"/>
<ListBox x:Name="listBox" HorizontalAlignment="Left" Margin="10,10,0,36.667" Width="119" ItemsSource="{Binding ReportItems}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel HorizontalAlignment="Left" Height="274" Margin="134,10,0,0" VerticalAlignment="Top" Width="375" DataContext="{Binding SelectedReportItem}">
<StackPanel.Resources>
<Style x:Key="ControlBaseStyle" TargetType="{x:Type Control}">
<Setter Property="Margin" Value="0, 10, 0, 0" />
</Style>
</StackPanel.Resources>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name:"/>
<TextBox Width="150" Text="{Binding Name}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Data File:"/>
<TextBox Width="150" Text="{Binding ID}"/>
</StackPanel>
<DataGrid Height="190" VerticalAlignment="Bottom" ItemsSource="{Binding TItems}"/>
</StackPanel>
<Button x:Name="button_Copy" Content="Save" HorizontalAlignment="Right" Margin="0,0,92,10" VerticalAlignment="Bottom" Width="75"/>
</Grid>
</Window>
My ViewModel:
public class MainViewModel
{
public ObservableCollection<ReportItem> ReportItems { get; set; }
public object SelectedReportItem { get; set; }
public MainViewModel()
{
ReportItems = new ObservableCollection<ReportItem>();
ReportItems.Add(Example);
}
public ReportItem Example = new TextReportItem() { Name = "John", DataFile = "try.txt"};
}
ReportItem:
public class ReportItem
{
public int Id { get; set; }
public string Name { get; set; }
public string DataFile { get; set; }
}
TextReportItem:
public class TextReportItem : ReportItem
{
public ObservableCollection<TextParcel> TItems { get; set; }
}
public class TextParcel
{
char Delimiter { get; set; }
string LineExp { get; set; }
string Result { get; set; }
string IgnoreLine { get; set; }
int DesiredResultIndexInLine { get; set; }
}
EDIT: as I use MVVM, I prefer to use only XAML in the View, with no code behind.
EDIT 2:
Thanks to S.Akbari I succeeded to view the desired properties in the TextBox controls, with the following code:
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name:"/>
<TextBox Width="150" Text="{Binding ElementName=listBox, Path=SelectedItem.Name}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Data File:"/>
<TextBox Width="150" Text="{Binding ElementName=listBox, Path=SelectedItem.DataFile}"/>
</StackPanel>
But when the same logic is applied to my DataGrid, it fails for some reason:
<DataGrid Height="190" VerticalAlignment="Bottom" ItemsSource="{Binding ElementName=listBox, Path=SelectedItem.TItmes}" />
I also tried:
<DataGrid Height="190" VerticalAlignment="Bottom" DataContext="{Binding ElementName=listBox, Path=SelectedItem}" ItemsSource="{Binding TItems}"/>
And also:
<DataGrid Height="190" VerticalAlignment="Bottom" DataContext="{Binding ElementName=listBox, Path=SelectedItem}">
<DataTemplate>
<TextBlock Text="{Binding TItems}" />
</DataTemplate>
</DataGrid>
if you use MVVM your view model should raise property changed events
You should implement INotifyPropertyChanged
and change the selected item to be a full property
see :How to: Implement Property Change Notification
XAML
<Window x:Class="Html5Mapper.Mapper.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpt="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="HTML mapper" Height="300" Width="600" >
<Window.DataContext>
<Binding Path="Main" Source="{StaticResource Locator}"/>
</Window.DataContext>
<ListBox Name="lbFiles" ItemsSource="{Binding Filescollection, Mode=OneWay}" Width="240" Height="220">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Image Source="HTML.png" Height="40" Width="32" />
<TextBlock Grid.Column="1" Text="{Binding Name}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MainViewModel.cs
public ObservableCollection<Files> Filescollection { get; set; }
public MainViewModel()
{
this.Filescollection = new ObservableCollection<Files>();
SelectFilesAction();
}
private void SelectFilesAction()
{
this.Filescollection.Add(new Files { Name = "index.html", Path = "C:\\Files"});
//lbFiles.ItemsSource = docs;
}
Q: What else do I need to get the docs object into the Listbox ?
In my opinion you are binding your controls to wrong datacontect, check output window for erros. You might want to set datacontext of main window to your MainViewModel (in codebehind or similar). Also why do you create another instance for docs? It is useless.
public ObservableCollection<Files> Filescollection {get;set;}
public MainViewModel()
{
this.Filescollection = new ObservableCollection<Files>();
SelectFilesAction();
}
private void SelectFilesAction()
{
Filescollection.Add(new Files { Name = "index.html", Path = "C:\\Files"});
}
Vidas is correct in that the DataContext of your Window is wrong, it needs to be your MainViewModel class.
Get rid of this:
<Window.DataContext>
<Binding Path="Main" Source="{StaticResource Locator}"/>
</Window.DataContext>
And add this to the Window tag:
<Window DataContext="{StaticResource MainViewModel}">
And that should do it.
<UserControl x:Class="RetailPOS.View.Usercontrols.MainWindow.Products"
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:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
xmlns:ctrl="clr-namespace:RetailPOS.View.Usercontrols.MainWindow"
d:DesignHeight="200" d:DesignWidth="490" **DataContext="{Binding Source={StaticResource Locator}, Path=CategoryVM}"**
xmlns:my="clr-namespace:WpfLab.Controls;assembly=WpfLab.Controls">
<UserControl.Resources>
</UserControl.Resources>
<Grid Width="490" Height="360">
<ListBox Name="LstProduct" ItemsSource="{Binding LstProduct}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="0" Height="Auto" Width="490" >
</WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Margin="1" Content="{Binding Name}" Height="53" Background="{Binding Color}" HorizontalAlignment="Right" Width="79"
Command="{Binding DataContext.ShowProductCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding}">
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
In the code behind
private ObservableCollection<ProductDTO> _lstProduct;
public ObservableCollection<ProductDTO> LstProduct
{
get { return _lstProduct; }
set
{
_lstProduct = value;
RaisePropertyChanged("LstProduct");
}
}
/// <summary>
/// Get all Commonly Used Products
/// </summary>
/// <returns>returns list of all Commonly Used products present in database</returns>
private void FillCommonProducts()
{
LstProduct = new ObservableCollection<ProductDTO>(from item in ServiceFactory.ServiceClient.GetCommonProduct()
select item);
}
I need to put some data in a DataGrid, using a custom UserControl for each row. Displaying the data works just fine, but when I edit the fields in the custom UserControl, the bound data records don't change.
If I use a ListBox to display the data instead, it all works as expected. But I would rather use the DataGrid, which allows for sorting and (hopefully) adding new records.
To illustrate, here is a simple data class that I need to display (and edit) - persons and their spouses:
public class PersonViewModel : INotifyPropertyChanged
{
public PersonViewModel(string name)
{
_name = name;
}
private string _name = null;
public string Name
{
get { return _name; }
set
{
_name = value;
this.PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
private PersonViewModel _spouse = null;
public PersonViewModel Spouse
{
get { return _spouse; }
set
{
_spouse = value;
this.PropertyChanged(this, new PropertyChangedEventArgs("Spouse"));
}
}
public event PropertyChangedEventHandler PropertyChanged = (s, e) => { };
}
..and here is the custom UserControl (PersonView):
<UserControl x:Class="TestDataGrid.PersonView"
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" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!--Editing in DataGrid works only if these bindings use UpdateSourceTrigger=PropertyChanged-->
<TextBox Text="{Binding Path=Name}" Width="70" />
<TextBlock Text="is married to" Margin="6,0" Grid.Column="1" />
<TextBox Text="{Binding Path=Spouse.Name}" Width="70" Grid.Column="2" />
</Grid>
</UserControl>
Finally, the main window (with code-behind) to put it all together:
<Window x:Class="TestDataGrid.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestDataGrid"
Title="MainWindow" Height="350" Width="500">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!--Changes made here correctly update the persons' names,
and the changes are reflected in the DataGrid below-->
<ListBox x:Name="_listbox" >
<ListBox.ItemTemplate>
<DataTemplate>
<local:PersonView />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!--Changes made here never reach the PersonViewModels..-->
<DataGrid x:Name="_datagrid" Grid.Column="1"
AutoGenerateColumns="False" CanUserAddRows="True" IsReadOnly="False" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Name}" Header="Name" />
<DataGridTextColumn Binding="{Binding Path=Spouse.Name}" Header="Spouse" />
</DataGrid.Columns>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" >
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate>
<local:PersonView />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
</DataGrid>
</Grid>
</Window>
...
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var personA = new PersonViewModel("Alice");
var personB = new PersonViewModel("Barry");
var personC = new PersonViewModel("Carl");
var personD = new PersonViewModel("Doris");
personA.Spouse = personB;
personC.Spouse = personD;
var persons = new List<PersonViewModel>() { personA, personC };
_listbox.ItemsSource = persons;
_datagrid.ItemsSource = persons;
}
}
What can I do to make editing work in the DataGrid, as it does in the ListBox?
I twiddled with your code a bit and I think I got it working.
First of all, you have a note on your PersonView that says "Editing in DataGrid works only if these bindings use UpdateSourceTrigger=PropertyChanged." I found that it WAS necessary to set UpdateSourceTrigger, but I used LostFocus to try and keep the same behavior as is in the ListBox.
<!--Editing in DataGrid works only if these bindings use UpdateSourceTrigger-->
<TextBox Text="{Binding Path=Name, UpdateSourceTrigger=LostFocus}" Width="70" />
<TextBlock Text="is married to" Margin="6,0" Grid.Column="1" />
<TextBox Text="{Binding Path=Spouse.Name, UpdateSourceTrigger=LostFocus}" Width="70" Grid.Column="2" />
Second, instead of using 2 DataGridTextColumns and DataGrid.RowStyle, I used a single DataGridTemplateColumn. I hope this is acceptable--it appears the way you had it before, the template wasn't really honoring the columns, but it does now.
<DataGrid x:Name="_datagrid" Grid.Column="1" AutoGenerateColumns="False" CanUserAddRows="True" IsReadOnly="False" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="People">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:PersonView />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
After making these changes, I saw it updating on both sides.
I hope that helps you.