Multiple Views for a single ViewModel - c#

Issue
I have a number of buttons in my WPF Window which when clicked need to change the view on the Window but keep the same ViewModel. Yesterday I tried using ControlTemplate for this but people mentioned I was better using a DataTemplate.
I need the binding to happen Via the ViewModel as well as I need to do some checks to see if the user can access the view.
Code
This is some of the code i started to write but I feel like its incorrect.
Here is the DataTemplate that I have defined in my view in Window.Resources:
<DataTemplate x:Key="panel1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="7*"/>
<ColumnDefinition Width="110*"/>
<ColumnDefinition Width="190*"/>
<ColumnDefinition Width="110*"/>
<ColumnDefinition Width="202*"/>
<ColumnDefinition Width="109*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="74*"/>
<RowDefinition Height="50*"/>
<RowDefinition Height="12*"/>
<RowDefinition Height="39*"/>
<RowDefinition Height="11*"/>
<RowDefinition Height="38*"/>
<RowDefinition Height="5*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Column="2" Grid.ColumnSpan="3" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label Content="Video Set:" Foreground="#e37e6e" Grid.Column="2" Grid.ColumnSpan="3" VerticalAlignment="Center" FontSize="22" HorizontalAlignment="Center"/>
<Image Source="{Binding VideoSet}" Height="25" Width="25" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
<TextBlock Foreground="#e37e6e" FontSize="12" Text="You currently do not have a video set. Please click the button below to add a video. Please note you will not be able to create an On Demand presentation without a media file selected. " Grid.Row="1" Grid.Column="2" TextWrapping="WrapWithOverflow" TextAlignment="Center" Grid.ColumnSpan="3" />
<Button Style="{StaticResource loginButton}" Command="{Binding ScreenBack}" Foreground="White" Content="Add Video" Grid.Column="3" HorizontalAlignment="Stretch" Grid.Row="3" VerticalAlignment="Stretch" Grid.ColumnSpan="1"></Button>
</Grid>
</DataTemplate>
Then I tried to use a ContentPresenter and bind to the DataTemplate:
<ContentPresenter Content="{Binding}" Grid.Row="3" Grid.RowSpan="1" Grid.ColumnSpan="5"/>
Now I want to be able to bind different DataTemplates to the ContentPresenter Via the ViewModel, can anyone help me with this issue?
EDIT:
I can bind the ContentPresenter to the DataTemplate through the static resource like below:
<ContentPresenter ContentTemplate="{StaticResource panel1}" Content="{StaticResource panel1}" Grid.Row="3" Grid.RowSpan="1" Grid.ColumnSpan="5"/>
The DataTemplate like below:
<DataTemplate x:Key="panel1">
</DataTemplate>
But how can i change the ControlPresenter binding from the ViewModel?
EDIT:
Here is my code cycle:
So here are the two DataTemplates:
<DataTemplate DataType="{x:Type local:ViewModelA}">
<TextBlock Foreground="#e37e6e" FontSize="12" Text="You currently do not have a video set. Please click the button below to add a video. Please note you will not be able to create an On Demand presentation without a media file selected. " Grid.Row="1" Grid.Column="2" TextWrapping="WrapWithOverflow" TextAlignment="Center" Grid.ColumnSpan="3" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelB}">
<TextBlock Foreground="#e37e6e" FontSize="12" Text="NEWWWWWWWWWWYou" Grid.Row="1" Grid.Column="2" TextWrapping="WrapWithOverflow" TextAlignment="Center" Grid.ColumnSpan="3" />
</DataTemplate>
The my ContentControl:
<ContentControl Content="{Binding SelectedViewModel}" />
I defined my DataContext in the code behind:
WizardViewModel _wizardViewModel = new WizardViewModel();
this.DataContext = _wizardViewModel;
In the WizardViewModel i have:
namespace Podia2016.ViewModels
{
public class WizardViewModel : INotifyPropertyChanged
{
public object SelectedViewModel { get; set; }
ViewModelA s = new ViewModelA();
ViewModelB d = new ViewModelB();
public WizardViewModel()
{
SelectedViewModel = s;
OnPropertyChanged("SelectedViewModel");
}
//BC - BINDS TO CHANGE LECTURE.
public ICommand Next
{
get { return new DelegateCommand<object>(Next_Click); }
}
private void Next_Click(object obj)
{
SelectedViewModel = d;
OnPropertyChanged("SelectedViewModel");
}
}
public class ViewModelA : INotifyPropertyChanged
{
//BC - DEFAULT ONPROPERTYCHANGED EVENT.
public event PropertyChangedEventHandler PropertyChanged;
public ViewModelA()
{
}
/// <summary>
/// This is the standard OnPropertyChanged Event Method
/// </summary>
/// <param name="name"></param>
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public class ViewModelB : INotifyPropertyChanged
{
//BC - DEFAULT ONPROPERTYCHANGED EVENT.
public event PropertyChangedEventHandler PropertyChanged;
public ViewModelB()
{
}
/// <summary>
/// This is the standard OnPropertyChanged Event Method
/// </summary>
/// <param name="name"></param>
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}

Data templating is much simpler to use (compared to your attempt):
create sub viewmodels, one for each sub view you want to have
// could have base class, may have to implement INotifyPropertyChanged, etc.
public class ViewModelA { }
public class ViewModelB
{
public string SomeProperty { get; }
}
...
public object SelectedViewModel { get; set; }
define data templates
<SomeContainer.Resources>
<!-- using user control -->
<DataTemplate DataType="{x:Type local:ViewModelA}">
<local:UserControlA />
</DataTemplate>
<!-- or like this -->
<DataTemplate DataType="{x:Type local:ViewModelB}">
<Grid>
<TextBlock Text="{Binding SomeProperty}" />
<Button .../>
...
</Grid>
</DataTemplate>
Bind content control to a property which select sub viewmodel
<ContentControl Content="{Binding SelectedViewModel}" />
control what to display by changing selected sub viewmodel:
var a = new ViewModelA();
var b = new ViewModelB() { SomeProperty = "Test" };
// display a - will display UserControlA content in ContentControl
SelectedViewModel = a;
OnPropertyChanged(nameof(SelectedViewModel));
// display b - will display text and button in ContentControl
SelectedViewModel = b;
OnPropertyChanged(nameof(SelectedViewModel));

Store DataTemplate as property of your ViewModel. Access the DataTemplate from ResourceDictionary to store in your property.
Bind <ContentPresenter Content="{Binding}" ContentTemplate="{Binding template1}" .../>
How to access ResourceDictionary from code :
If you have in your WPF project an ResourceDictionary that you use to define resources you can create an instance of it from code like this:
ResourceDictionary res = Application.LoadComponent(
new Uri("/WpfApplication1;component/MyDataTemplateCollection.xaml",
UriKind.RelativeOrAbsolute)) as ResourceDictionary;
Where WpfApplication1 is name of your assembly and MyDataTemplateCollection.xaml is name of your ResourceDictionary.
Another way is to use the code-behind for the resource dictionary.
Add x:Class to your ResourceDictionary:
Add class MyDataTemplateCollection.xaml.cs as your code-behind for the ResourceDictionary.
The code behind class looks like so:
partial class MyDataTemplateCollection: ResourceDictionary
{
public MyDataTemplateCollection()
{
InitializeComponent();
}
}
Usage :
ResourceDictionary res = new MyDataTemplateCollection();

Related

How to bind command and contexrt from UserControl to MainView UWP MVVM

There is a form in which the label is located, as well as a user control wrapped in a button. I need to change the text in the TextBlock by clicking the button in the user control. I didn't use any MVVM frameworks.
Main XAML:
<Page.DataContext>
<local:MainViewModel />
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="553*" />
<ColumnDefinition Width="197*" />
</Grid.ColumnDefinitions>
<local:ControlView Grid.Column="1" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
<TextBlock Grid.Column="0" Foreground="Black" FontSize="50" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" Text="{Binding UserText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
User Conrol Xaml:
<Grid>
<Button Width="300" Height="100" FontSize="50" Command="{Binding}"
VerticalAlignment="Center" HorizontalAlignment="Center" Content="Print chart" />
</Grid>
Main VM:
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
SetText = new RelayCommand(SetUserText);
}
public ICommand SetText { get; set; }
public string UserText { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void SetUserText()
{
UserText = "Hello World!";
RaisePropertyChanged(nameof(UserText ));
}
protected void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
And I create an empty user control VN:
public class ControlViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
// And what should I do here?
}
So how to correctly implement commands from user control to main view, f.e SetText property?
Your main issue is there is the use of the ControlViewModel. UserControls such as ControlView should generally speaking not set their own DataContext to some specialized view model but rather inherit the DataContext from their parent element.
Assuming you let the control inherit the DataContext, you could add a dependency property to it and bind the command to this one:
<local:ControlView YourCommand="{Binding SetText}" />
ControlView.xaml:
<UserControl ... Name="uc">
<Button Command="{Binding YourCommand, ElementName=uc}" />

Define command binding in user control

I wrote user control with 2 buttons and one check box and now I want to bind Commands to data context - for each button and checkbox.
But I don't know how to define command binding. I think I'll need some kind of ICommand property in User control - but how can I connect user's data context command delegate? I want to use user control to manage each item in collection like this:
<ItemsControl ItemsSource="{Binding Path=MoneyInfo}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ChannelSetupControl
CurrentCount="{Binding Count}"
CoinValue="{Binding Value}"
UpCommand="{Binding DataContextUp}"
DownCommand="{Binding DataContextDown}"
ChangeCheckboxCommand="{Binding DataContextChange}"></local:ChannelSetupControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
XAML User control
<UserControl>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="3*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Text="{Binding CoinValue}" TextAlignment="Center"></TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1" Text="{Binding CurrentCount, Mode=TwoWay}" TextAlignment="Center" VerticalAlignment="Center" FontSize="30"></TextBlock>
<StackPanel Grid.Column="1" Grid.Row="1" VerticalAlignment="Center">
<Button Content="+ 10" Padding="0 5"></Button>
<Button Content="- 10" Padding="0 5"></Button>
</StackPanel>
<CheckBox Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" IsChecked="{Binding Cycling, Mode=TwoWay}" Content="recycling" VerticalContentAlignment="Center"></CheckBox>
</Grid>
</UserControl>
and code behind and this is where I'm lost - how to define UpCommand, DownCommand and ChangeCheckboxCommand?
public partial class ChannelSetupControl : UserControl, INotifyPropertyChanged
{
private int currentCount;
private bool cycling;
private double coinValue;
public int Step { get; set; }
public double CoinValue { get { return coinValue; } set { coinValue = value; NotifyPropertyChanged("CoinValue"); } }
public int CurrentCount { get { return currentCount; } set { currentCount = value; NotifyPropertyChanged("CurrentCount"); } }
public bool Cycling { get { return cycling; } set { cycling = value; NotifyPropertyChanged("Cycling"); } }
public ChannelSetupControl()
{
InitializeComponent();
DataContext = this;
CurrentCount = 0;
Step = 10;
Cycling = false;
CoinValue = 0;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
First of all your ChannelSetupControl class extends UserControl, so it implicitly extends DependencyObject class. It means you can use Dependency Properties instead of implementing INotifyPropertyChanged.
So you can define a dependency property in your ChannelSetupControl class, like this one:
public static readonly DependencyProperty UpCommandProperty =
DependencyProperty.Register("UpCommand", typeof(ICommand), typeof(ChannelSetupControl));
public ICommand UpCommand
{
get { return (ICommand)GetValue(UpCommandProperty); }
set { SetValue(UpCommandProperty, value); }
}
At the same time in your control XAML:
<Button Command="{Binding RelativeSource={RelativeSource Mode=Self}, Path=UpCommand, Mode=OneWay}"
Content="+ 10" Padding="0 5" />
In this way in your window XAML you can wrote:
<local:ChannelSetupControl UpCommand="{Binding UpCommand, Mode=OneWay}" ... />
You can use the same "pattern" for the other controls.
Regarding ICommand, there are a lot of implementations. The one that I prefer is the so called delegate command (for a sample you can take a look here).
I hope this quick explanation can help you.

Listbox with UserControl ItemTemplate always shows default values

To start off, I have a listbox that is trying to accept a UserControl as the DataTemplate:
<ListBox VerticalAlignment="Stretch" Name="GeneralMcmView" Grid.Column="0" HorizontalAlignment="Stretch" >
<ListBox.ItemTemplate>
<DataTemplate DataType="local:GeneralMcmMessage">
<local:GeneralMcmMessage />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
With the contents of the usercontrol looking like:
<ContentControl FontFamily="Segoe UI" VerticalAlignment="Stretch" FontSize="10">
<Grid VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal" Grid.Row="0">
<TextBlock Name="MessageDateTime" Text="{Binding ElementName=_this, Path=TimeStamp, StringFormat=MM/dd/yyyy h:mm:ss.fff tt \'GMT\' (zzz)}" />
<TextBlock Name="MessageTypeLabel" Margin="15,0,5,0" Text="Type:"/>
<TextBlock Name="MessageType" Text="{Binding ElementName=_this, Path=Type}" />
</StackPanel>
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal" Grid.Row="1">
<TextBlock Name="MessageNameLabel" Margin="0,0,5,0" Text="Message Name:" />
<TextBlock Name="MessageNameValue" Text="{Binding ElementName=_this, Path=MessageName}" TextWrapping="Wrap" />
</StackPanel>
<StackPanel VerticalAlignment="Stretch" Orientation="Vertical" Grid.Row="2">
<TextBlock Name="MessageLabel" Text="Message:"/>
<TextBlock Name="Message" Margin="10,0,0,0" Text="{Binding ElementName=_this, Path=MessageContent}" />
</StackPanel>
</Grid>
</ContentControl>
I then create a couple messages, all with different data (The Listbox's ItemSource is bound to the GeneralMessages ObservableCollection):
GeneralMcmMessage newMsg = new GeneralMcmMessage()
{
MessageId = e.McmMessageViewInfo.Id,
TimeStamp = e.McmMessageViewInfo.MessageDateTime,
Type = e.McmMessageViewInfo.MessageType.ToString(),
MessageName = e.McmMessageViewInfo.MessageName,
MessageContent = e.McmMessageViewInfo.Message.ToString()
};
GeneralMessages.Add( newMsg );
During runtime I interrogate the Items property of the listbox and all the data looks correct, however all I see in the listbox are entries of the GeneralMcmMessage User Control with default data values. Any ideas as to why?
Also, FWIW I am using INotifyPropertyChanged in the usercontrol class:
public partial class GeneralMcmMessage : UserControl, INotifyPropertyChanged
{
private Constants.PiuModule piuModule = Constants.PiuModule.MCM;
private string className = "GeneralMcmMessage";
/// <summary>
/// Event for notifying listeners that a property changed. Part of INotifyPropertyChanged
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
public int MessageId { get; set; }
private DateTime timeStamp;
public DateTime TimeStamp
{
get
{
return timeStamp;
}
set
{
timeStamp = value;
OnNotifyPropertyChanged( "TimeStamp" );
}
}
You're saying that the DataTemplate to display a GeneralMcmMessage is to instantiate new GeneralMcmMessage.
<DataTemplate DataType="local:GeneralMcmMessage">
<local:GeneralMcmMessage />
</DataTemplate>
I'd recommend instead of making a collection of your UserControl to make a collection of your model object instead, and bind to the properties within that within your UserControl.
Either way though - the object you created in code will be the DataContext for the one you created in XAML so removing the ElementName=_this should bind appropriately. Try this simplified XAML in your UserControl instead.
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal" Grid.Row="0">
<TextBlock Name="MessageDateTime" Text="{Binding TimeStamp, StringFormat=MM/dd/yyyy h:mm:ss.fff tt \'GMT\' (zzz)}" />
<TextBlock Name="MessageTypeLabel" Margin="15,0,5,0" Text="Type:"/>
<TextBlock Name="MessageType" Text="{Binding Type}" />
</StackPanel>
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal" Grid.Row="1">
<TextBlock Name="MessageNameLabel" Margin="0,0,5,0" Text="Message Name:" />
<TextBlock Name="MessageNameValue" Text="{Binding MessageName}" TextWrapping="Wrap" />
</StackPanel>
<StackPanel VerticalAlignment="Stretch" Orientation="Vertical" Grid.Row="2">
<TextBlock Name="MessageLabel" Text="Message:"/>
<TextBlock Name="Message" Margin="10,0,0,0" Text="{Binding MessageContent}" />
</StackPanel>
You don’t post all code of GeneralMcmMessage hence I don’t know if you set DataContext in user control for example in constructor of GeneralMcmMessage.
I tried replicate your problem.
User control GeneralMcmMessage code behind
public partial class GeneralMcmMessage : UserControl, INotifyPropertyChanged
{
private int _messageId;
public int MessageId
{
get
{
return _messageId;
}
set
{
_messageId = value;
OnPropertyChanged("MessageId");
}
}
private DateTime _timeStamp;
public DateTime TimeStamp
{
get
{
return _timeStamp;
}
set
{
_timeStamp = value;
OnPropertyChanged("TimeStamp");
}
}
public GeneralMcmMessage()
{
InitializeComponent();
//don’t set data context here
//DataContext = this;
}
}
User control GeneralMcmMessage XAML
<StackPanel>
<TextBlock Margin="5" FontSize="20" Text="{Binding Path=MessageId}"/>
<TextBlock Margin="5" FontSize="20" Text="{Binding Path=TimeStamp}"/>
</StackPanel>
User control GeneralMcmMessage usage
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<GeneralMcmMessage> _generalMessages;
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public ObservableCollection<GeneralMcmMessage> GeneralMcmMessages
{
get { return _generalMessages; }
set
{
_generalMessages = value;
OnPropertyChanged("GeneralMcmMessages");
}
}
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
GeneralMcmMessages = new ObservableCollection<GeneralMcmMessage>();
for (int i = 0; i < 10; i++)
{
var newMsg = new GeneralMcmMessage
{
MessageId = i,
TimeStamp = DateTime.Now,
};
GeneralMcmMessages.Add(newMsg);
}
}
}
User control GeneralMcmMessage usage XAML
<ListBox x:Name="ListBox"
Margin="5"
ItemsSource="{Binding Path=GeneralMcmMessages}">
<ListBox.ItemTemplate>
<DataTemplate DataType="stackoverflow:GeneralMcmMessage">
<stackoverflow:GeneralMcmMessage/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If this not solve your problem could you please post all GeneralMcmMessage?
If you want I can upload sample project for you.
Thank you

How to bind form Code activity to XAML?

I'm trying to make an activity designer library. I have two sources; one of them is CodeActivity in C# code and the other one is Activity designer in XAML. In CodeActivity, I have a public property Name. In XAML, I want to view and change it's value through binding. My XAML design is like this
I declared Name property like this:
private string _name;
public string Name {
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
And my XAML is like this:
...
<DataTemplate x:Key="Expanded">
<StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="130"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="txtName" Grid.Column="1" Grid.Row="0" Text="{Binding Name, Mode=TwoWay}"/>
<TextBlock Grid.Column="0" Grid.Row="0" Text="Name :" HorizontalAlignment="Right"/>
</Grid>
<sap:WorkflowItemPresenter Item="{Binding Path=ModelItem.Body, Mode=TwoWay}"
HintText="Please drop an activity here" />
</StackPanel>
</DataTemplate>
I've tried a lot of ways, but I couldn't do it.
How can I show the Name property from CodeActivity in XAML?
I got it.
When we want to bind a variable from CodeActivity Side to XAML, we do like this :
...
xmlns:s="clr-namespace:System;assembly=mscorlib"
<sap:ActivityDesigner.Resources>
<sapc:ArgumentToExpressionConverter x:Key="ArgumentToExpressionConverter" />
...
<sapv:ExpressionTextBox HintText="Enter custom text here ..." Expression="{Binding Path=ModelItem.Text, Mode=TwoWay, Converter={StaticResource ArgumentToExpressionConverter}, ConverterParameter=In}" ExpressionType="s:String" OwnerActivity="{Binding Path=ModelItem}" MaxLines="1"/>
...

WPF Listview + GridView - View not Updating

I have got a Listview Item with a Gridview Child, bound to a List of Objects.
Below the Gridview I have got texboxes to edit the content of the Gridview (bound to the Gridview).
I can add new content (which is displayed in the GridView).
When i edit content, it is in fact edited (in the object list) but not displayed in the Gridview (the GridView does not seem to update)
xaml code:
<!-- ========= -->
<!-- root Grid -->
<!-- ========= -->
<Grid x:Name="root" Margin="10,10,10,10">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- ========= -->
<!-- Data Grid -->
<!-- ========= -->
<ListView x:Name="dataGrid" Grid.Row="0" Grid.ColumnSpan="2" ItemsSource="{Binding}">
<ListView.View>
<GridView>
<!-- first solution -->
<GridViewColumn x:Name="gridColumnName" Header="Name" Width="160">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<!-- second solution -->
<GridViewColumn x:Name="gridColumnPath" Header="Path" DisplayMemberBinding="{Binding Path=Path}" Width="490" />
</GridView>
</ListView.View>
</ListView>
<!-- ========= -->
<!-- Edit Menu -->
<!-- ========= -->
<Label Content="Name:" Grid.Row="1" Grid.Column="0" VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<TextBox x:Name="txtBoxName" Grid.Row="1" Grid.Column="1" Width="250" VerticalAlignment="Bottom" HorizontalAlignment="Left"
DataContext="{Binding ElementName=dataGrid, Path=SelectedItem}"
Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Label Content="Path:" Grid.Row="2" Grid.Column="0" VerticalAlignment="Bottom" HorizontalAlignment="Left" />
<TextBox x:Name="txtBoxPath" Grid.Row="2" Grid.Column="1" VerticalAlignment="Bottom" HorizontalAlignment="Stretch"
DataContext="{Binding ElementName=dataGrid, Path=SelectedItem}"
Text="{Binding Path=Path, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Object List class:
class ItemList : ObservableCollection<LdapItem>
{
public ItemList()
: base()
{
}
}
Object class:
class LdapItem : INotifyPropertyChanged
{
#region constructor
public LdapItem(String name, String path)
{
this.iD = Guid.NewGuid().ToString();
this.name = name;
this.path = path;
}
#endregion
#region public proterties
public String ID
{
get { return iD; }
}
public String Name
{
get { return name; }
set { name = value; }
}
public String Path
{
get { return path; }
set { path = value; }
}
#endregion
#region public methods
public void OnPropertyChanged(string prop)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
#endregion
#region private variables
private String name = String.Empty;
private String path = String.Empty;
private String iD = String.Empty;
#endregion
public event PropertyChangedEventHandler PropertyChanged;
}
any ideas why updating the GridView doesnt work?
If you have a number of models use a base class that implements INPC. Then change your property changed event handler to:
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
This will eliminate the need to specify the model property being changed. Reduces the number of misspelling errors or forgetting to put the name in. Still needs to call this.OnPropertyChanged() though which you are missing in several setters.
The ItemList class doesn't make sense. It could be replaced with:
public ObservableCollection<LdapItem> LdapItems
it seems like you forgot to fire the OnPropertyChangedEvent when your property changes:
public String Name
{
get { return name; }
set {
name = value;
OnPropertyChanged("Name");
}
}
If you don't fire the PropertyChanged event, WPF will not be able to see if the object has changed.

Categories