This question already has answers here:
ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)
(21 answers)
Closed 6 years ago.
I would greatly appreciate some help with this binding issue I'm having. Basically I have a list view showing some information about Files. In the list view item itself, there's some text and also a button.
When this button is clicked I want to disable that button.
Currently I've set up an ObservableCollection - however even though the button click is being registered, the UI doesn't update. If I go to a different screen and return, then the UI updates. So it's not instantaneous.
I think there is some problem with the way RaisePropertyChanged() is working. I know from reading other SO articles that property changes in the object are harder to pick up than say, removing an item or adding an item to the ListView.
I'm completely stuck, any help would be most appreciated. Thanks.
Xaml:
<ListView RelativePanel.Below="heading" ItemsSource="{Binding Pages}" ReorderMode="Enabled" CanReorderItems="True" AllowDrop="True" Margin="0,10" SelectedItem="{Binding Path=SelectedFile,Mode=TwoWay}" >
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:File">
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{x:Bind Path= Name, Mode=TwoWay}" FontWeight="Bold" Padding="0,5" />
<TextBlock Text ="{x:Bind Path}" Grid.Row="1" TextWrapping="Wrap" Padding="10,0,0,0" Foreground="DarkGray" Opacity="0.8" />
<Button Content="X" Grid.Column="1" Grid.RowSpan="2" Command="{x:Bind EnableCommand}" IsEnabled="{x:Bind Path=IsEnabled, Mode=OneWay}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
File.cs:
public class File : ViewModelBase
{
public string Name { get; set; }
public string FileName { get; set; }
public string Path { get; set; }
public string Contents { get; set; }
private Boolean isEnabled = true;
public Boolean IsEnabled {
get { return isEnabled; }
private set {
isEnabled = value;
RaisePropertyChanged("IsChecked");
}
}
private ICommand enableCommand;
public ICommand EnableCommand
{
get
{
if(enableCommand == null)
{
enableCommand = new RelayCommand(() => {
isEnabled = false;
Name += "Disabled";
RaisePropertyChanged();
});
}
return enableCommand;
}
}
}
Viewmodel:
public class MyPageViewModel : BaseViewModel
{
private ObservableCollection<File> pages;
public ObservableCollection<File> Pages
{
get { return pages; }
set
{
pages = value;
RaisePropertyChanged();
}
}
private File selectedFile = new File();
public File SelectedFile
{
get { return selectedFile; }
set
{
Set(ref selectedFile, value);
}
}
public MyPageViewModel()
{
if (ApplicationData.FileList != null)
{
Pages = new ObservableCollection<File>(ApplicationData.FileList);
}
else
{
Pages = new ObservableCollection<File>();
}
}
You notify IsChecked when you should be notifying IsEnabled.
(ObsevarvableCollection only notifies when something is added or removed from it. Changes in the objects it holds are not notified by it.)
Related
I am trying to create my own expander control.
It has a header and a body. The header is a button that shows or hides the body.
This is all in an <ItemsControl/> that is bound to an observable collection - Each object in the observable collection creates its own expander control.
For the most part, this is working. What I am having trouble with is the expanding part. When clicking on the header, all the controls show or hide their contents at the same time - I know what the problem is: All the properties are bound to a value at the same time. How do I make them operate independently from one another?
I am not even sure that the way I am trying to handle this is the best way (I could use the expander control but I am still learning WPF and XAML and would like to recreate some standard controls for practice and understanding). I am still trying to figure out how bindings work; mostly RelativeSource and everything associated with it.
I would also like to pass a selected object as a parameter through the x:name="btn_body" button.
My current XAML:
<ItemsControl ItemsSource="{Binding testName}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="parent" Margin="50,5" Background="Yellow"
Height="{Binding DataContext.ParentHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MainWindow}}">
<DockPanel x:Name="child_header" Height="50" DockPanel.Dock="Top" Background="Aqua">
<Button Height="50" x:Name="btn_header"
Command="{Binding DataContext.OpenCloseBoxCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MainWindow}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text=" CONTENT "/>
</StackPanel>
</Button>
</DockPanel>
<DockPanel x:Name="child_body" DockPanel.Dock="Top"
Visibility="{Binding DataContext.Visable, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MainWindow}}"
Height="{Binding DataContext.ChildHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MainWindow}}">
<Grid ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Button x:Name="btn_body" Grid.Column="2" Grid.Row="3" Grid.RowSpan="3" Margin="35,5" Content="CONFIRM"/>
</Grid>
</DockPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The ViewModel for the above (DataContext is set in the MainWindow code behind)
public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel()
{
string content = File.ReadAllText(#"C:\\tempfiles\\people.json");
var listOfPeople = JsonConvert.DeserializeObject<apiResponse>(content);
testName = new ObservableCollection<person>(listOfPeople.people);
OpenCloseBoxCommand = new RelayCommand(OpenClose);
}
public RelayCommand OpenCloseBoxCommand { get; set; }
public ObservableCollection<person> testName { get; set; }
private int _parentheight = 150;
public int ParentHeight
{
get { return _parentheight; }
set { _parentheight = value; }
}
private Visibility _visable = Visibility.Visible;
public Visibility Visable
{
get { return _visable; }
set { _visable = value; }
}
private int _childheight = 100;
public int ChildHeight
{
get { return _childheight; }
set { _childheight = value; }
}
public void OpenClose()
{
if(_parentheight == 150)
{
_parentheight = 50;
_visable = Visibility.Hidden;
OnPropertyChanged("ParentHeight");
} else
{
_parentheight = 150;
_visable = Visibility.Visible;
OnPropertyChanged("ParentHeight");
}
}
}
EDIT
I have moved the properties over to the person class, along with the command. The general functionality works as expected now, but, Is the following the correct way to do things?
I understand the idea behind the MVVM pattern and as such I am trying to keep the UI stuff away from everything else as what I am trying to achieve is purely UI logic and non critical to functionality (this expander could be replaced by a grid and everything would function normally).
It just seems that UI "logic" is creeping into where it shouldn't be.
public class person : ViewModelBase { //needed for OnPropertyChanged
public person()
{
OpenCloseBoxCommand = new RelayCommand(OpenClose);
}
public RelayCommand OpenCloseBoxCommand { get; set; }
public string PersonId { get; set; }
public string FirstName { get; set; }
public string SecondName { get; set; }
private int _parentheight = 150;
public int ParentHeight
{
get { return _parentheight; }
set { _parentheight = value; }
}
private Visibility _visable = Visibility.Visible;
public Visibility Visable
{
get { return _visable; }
set { _visable = value; }
}
private int _childheight = 100;
public int ChildHeight
{
get { return _childheight; }
set { _childheight = value; }
}
public void OpenClose()
{
if (_parentheight == 150)
{
_parentheight = 50;
_visable = Visibility.Hidden;
OnPropertyChanged("ParentHeight");
}
else
{
_parentheight = 150;
_visable = Visibility.Visible;
OnPropertyChanged("ParentHeight");
}
}
}
ParentHeight and Visable should be property of person,not MainWindowViewModel.
In addition, as a member of MainWindowViewModel,OpenCloseBoxCommand should be added a parameter for pass current item.
I want to know why do not you choose Expander control?
I ever implemennt a Expander in Listbox,it worked well.
I am new to C#/WPF. There is a view with one button defined, when the view is initialized, buttons will display a set of reason codes got from DataContext (viewmodel), once any button is clicked, the code on it will be saved and passed forward for next processing.
Q: The text on buttons are totally empty, but the clicked code can be captured, so where the problem is about binding? Thanks.
XAML:
<Button x:Name="btnReason" Command="{Binding DataContext.SelectCommand, RelativeSource={RelativeSource AncestorType=v:View, Mode=FindAncestor}}" CommandParameter="{Binding}" Width="190" Height="190" >
<Border Background="Transparent">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock x:Name="Reason" Grid.Row="0" Text="{Binding ?????}" TextWrapping="Wrap" />
</Grid>
</Border>
</Button>
The code on C#:
public class ReasonsViewModel : ViewModel
{
private IEnumerable<string> m_Names;
public IEnumerable<string> Names
{
get { return m_Names; }
set
{
if (m_Names != value)
{
m_Names = value;
OnPropertyChanged(() => Names);
}
}
}
private string m_SelectedName;
public string SelectedName
{
get { return m_SelectedName; }
set
{
if (m_SelectedName != value)
{
m_SelectedName = value;
OnPropertyChanged(() => SelectedName);
}
}
}
public DelegateCommand SelectCommand { get; private set; }
public ReasonsViewModel()
{
SelectCommand = new DelegateCommand(p => SelectCommandExecute(p));
}
private bool m_Processing;
private void SelectCommandExecute(object item)
{
if (m_Processing) return;
try
{
m_Processing = true;
var name = item as string;
if (name == null) return;
SelectedName = name;
}
finally
{
m_Processing = false;
}
}
}
If I understood your question correctly than your property text in your TextBlock should be bound to SelectedName.
The problem is that your CommandParameter is bound to DataContext. That's what an empty {Binding} statement bounds to. This means your command handler always returns after the null check.
I also suggest that you change your Names proeprty from IEnumerable<string> to ObservableCollection<string>.
ObservableCollection raises events on any additions or removalof items inside and WPF components can bind to these events.
I am developing an application using c# and the Universal Windows Platform (UWP) and am struggling with creating a one-way data-bind between a layout control and an observable class. Currently, when the observable class property is changed, it does not update the UI element. I think it has something to do with the fact that I am binding a DataTemplate ListViewItem rather than a static layout element, but I am not sure if this is the problem or how to solve it. Any help would be appreciated. The code for the UI element and backend code is shown.
DataTemplate (XAML) (Styling is removed for readability)
<DataTemplate x:Key="variableTemplate"
x:DataType="local:VariableNode">
<Border>
<StackPanel Orientation="Vertical">
<Border>
<Grid>
<TextBlock Text="{Binding Name}" />
<StackPanel Orientation="Horizontal" >
<Button Tag="{Binding Description}"/>
<Button Tag="{Binding}"/>
</StackPanel>
</Grid>
</Border>
<Grid Margin="0, 10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Border >
<Grid Grid.Column="0">
<Button Click="Choose_Measurement"
Tag="{Binding}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{x:Bind Path=Measurement_Name, Mode=TwoWay}"
Foreground="{x:Bind MF}" />
<TextBlock Foreground="{x:Bind MF}" />
</StackPanel>
</Button>
</Grid>
</Border>
<Grid Grid.Column="1">
<Button Foreground="{Binding UF}"
Tag="{Binding}"
IsEnabled="{Binding Unit_Exists}"
Click="Choose_Unit">
<StackPanel Orientation="Vertical">
<TextBlock Text="{x:Bind Path=Unit_Name, Mode=OneWay}"
Foreground="{Binding UF}" />
<TextBlock Foreground="{Binding UF}" />
</StackPanel>
</Button>
</Grid>
</Grid>
</StackPanel>
</Border>
</DataTemplate>
C# Observable Class VariableNode (Irrelevant properties removed)
public class VariableNode : ExperimentNode
{
public VariableNode() { }
public VariableNode(VariableType type)
{
Type = type;
Name = name_ref[(int)Type];
Category = "Problem";
Unit = -1;
}
private string[] name_ref = { "Independent Variable", "Dependent Variable", "Controlled Variable" };
public enum VariableType { Independent, Dependent, Controlled };
public VariableType Type { get; set; }
public Measurement Measure { get; set; }
public int Unit { get; set; }
[XmlIgnoreAttribute]
public Measurement MeasureSource
{
get { return this.Measure; }
set
{
this.Measure = value;
OnPropertyChanged("Measurement_Name");
}
}
[XmlIgnoreAttribute]
public string Measurement_Name
{
get
{
if (Measure == null) { return "Select a Measurement"; }
else { return Measure.Name; }
}
set
{
if (Measure != null)
{
Measure.Name = value;
OnPropertyChanged();
}
}
}
[XmlIgnoreAttribute]
public string Unit_Name
{
get
{
if (Measure == null) { return "No measurement"; }
else if (Unit < 0) { return "Select a unit"; }
else { return Measure.Unit[Unit]; }
}
}
[XmlIgnoreAttribute]
public bool Unit_Exists
{
get { return Measure != null; }
}
}
C# XAML.CS code calling the property change
public void Choose_Measurement (object sender, RoutedEventArgs e)
{
Button butt = sender as Button
VariableNode sel = butt.Tag as VariableNode;
sel.Measurement_Name = "New Name";
}
Again thanks for the help, I know its a lot of code, and I appreciate the help in debugging / learning.
Ok, so I ended up finding the answer, and I think that it may help others trying to replicate what I am trying to do:
Basically, the class that one is trying to make observable must extend the class INotifyPropertyChanged. So, I ended up making a base class from which to extend all of my observable classes from:
public class BaseClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged(this, e);
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}
Hi all I have a list view which is filled by an ObservableCollection. Now I want to get the value of the selected item from the list and store it. How I can achieve this?
This is my ViewModel:
public StopViewModel(IGrtrService grtrService)
{
Argument.IsNotNull(() => grtrService);
_grtrService = grtrService;
AllStops = _grtrService.LoadStop();
Stop_Line = _grtrService.LoadLines();
SearchCollection = new Command(OnSearchPressed);
}
public ObservableCollection<Stop> AllStopsCollection // Must be property or DP to be bound!
{
get { return AllStops; }
set
{
if (Equals(value, AllStops)) return;
AllStops = value;
}
}
public Grtr Grtr
{
get { return GetValue<Grtr>(GrtrProperty); }
set { SetValue(GrtrProperty, value); }
}
public static readonly PropertyData GrtrProperty = RegisterProperty("Grtr", typeof(Grtr));
}
And in the XAML file I have the following code:
<catel:StackGrid x:Name="LayoutRoot">
<catel:StackGrid.ColumnDefinitions>
<ColumnDefinition />
</catel:StackGrid.ColumnDefinitions>
<catel:StackGrid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</catel:StackGrid.RowDefinitions>
<ToolBarTray Grid.Row="0" VerticalAlignment="Top" Background="Azure">
<ToolBar>
<TextBox Width="150" Text="{Binding Path=SearchValue}" />
<Button Content="Search" Command="{Binding SearchCollection}" />
<Button Content="Pass Object" Command="{Binding SearchCollection}" />
</ToolBar>
</ToolBarTray>
<ListBox Grid.Row="1" ItemsSource="{Binding AllStopsCollection}" SelectedValue="{Binding SelectedStop}" />
</catel:StackGrid>
Since you are using Catel, it will automatically take care of change notifications for your. Just define this property:
public Stop SelectedStop
{
get { return GetValue<Stop>(SelectedStopProperty); }
set { SetValue(SelectedStopProperty, value); }
}
public static readonly PropertyData SelectedStopProperty = RegisterProperty("SelectedStop", typeof(Stop));
It will be set to the value.
Pro tip: if you use Catel.Fody, you can write this:
public Stop SelectedStop { get; set; }
and it will automatically be converted to the final Catel property as written above.
In your ViewModel :
private stop _selectedStop;
public Stop SelectedStop
{
get
{
return _selectedStop;
}
set
{
if (_selectedStop!= value)
_selectedStop = value;
OnPropertyChanged("SelectedStop"); //U should implement this method using INotifyPropertyChanged
}
}
In your Window (XAML) , set the Binding's mode to TwoWay :
<ListBox Grid.Row="1" ItemsSource="{Binding AllStopsCollection}" SelectedValue="{Binding SelectedStop, Mode=twoWay}" />
As I see from comments you just can't figure out how to bind list's selected item to property. So first of all you need to create coresponding property in your view model:
public Stop SelectedStop
{
get
{
return _selectedStop;
}
set
{
if (Equals(value, _selectedStop)) return;
_selectedStop = value;
}
}
Make sure that you implement INotifyPropertyChanged interface and thet your property is raising "OnPropertyChanged" when it's changed.
And for the list box you should set:
<ListBox Grid.Row="1" ItemsSource="{Binding AllStopsCollection}" SelectedValue="{Binding SelectedStop, Mode=TwoWay}" />
I'm trying to write a windows phone 8 app using the mvvm pattern but I'm struggling with it.
I have a page with a list of persons which is binded to my PersonViewModel. That part is working fine. I then have 2 buttons in the application bar i.e. add or edit. When I want to edit a person, I select the person from the list, which then sets the CurrentPerson in my ViewModel. This in turns set a property in my MainViewModel which is used to store the currently selected person i.e.
App.MainViewModel.CurrentPerson = this.CurrentPerson;
When I want to add a new person, I use the same principal but I create a new person model.
App.MainViewModel.CurrentPerson = new PersonModel();
I then redirect to a page which contains the fields to handle a person, whether it is being added or edited and this is binded to a ViewModel called PersonEntryViewModel
Before I explain my problem, I want to let you know what I'm trying to achieve. I want the "Save" button in my application bar to get enabled once a certain amount of criteria have been met i.e. Name has been filled and has x characters, etc...
I can see what my problem is but I don't know how to resolve it.
Here is a simplied version of my PersonEntryViewModel:
public class PersonEntryViewModel : BaseViewModel
{
private PersonModel _currentPerson;
private bool _isNewPerson;
private ICommand _savePersonCommand;
private ICommand _cancelCommand;
private ICommand _titleTextChanged;
private bool _enableSaveButton;
public PersonEntryViewModel()
{
this.CurrentPerson = App.MainViewModel.CurrentPerson ?? new PersonModel();
}
public ICommand SavePersonCommand
{
get
{
return this._savePersonCommand ?? (this._savePersonCommand = new DelegateCommand(SavePersonAction));
}
}
public ICommand CancelCommand
{
get
{
return this._cancelCommand ?? (this._cancelCommand = new DelegateCommand(CancelAction));
}
}
public ICommand NameTextChanged
{
get
{
return this._nameTextChanged ?? (this._nameTextChanged = new DelegateCommand(NameTextChangedAction));
}
}
private void NameTextChangedAction(object actionParameters)
{
if (!string.IsNullOrEmpty(this._currentPerson.Name) && _currentPerson.Name.Length > 2)
{
EnableSaveButton = true;
}
}
private void CancelAction(object actionParameters)
{
Console.WriteLine("Cancel");
INavigationService navigationService = this.GetService<INavigationService>();
if (navigationService == null)
return;
navigationService.GoBack();
navigationService = null;
}
private void SavePersonAction(object actionParameters)
{
Console.WriteLine("Saving");
}
public PersonModel CurrentPerson
{
get { return this._currentPerson; }
set
{
if (this._currentPerson != value)
this.SetProperty(ref this._currentPerson, value);
}
}
public string PageTitle
{
get { return this._pageTitle; }
set { if (this._pageTitle != value) this.SetProperty(ref this._pageTitle, value); }
}
public bool IsNewPerson
{
get { return this._isNewPerson; }
set
{
if (this._isNewPerson != value)
{
this.SetProperty(ref this._isNewPerson, value);
if (this._isNewPerson)
this.PageTitle = AppResources.PersonEntryPageNewTitle;
else
this.PageTitle = AppResources.PersonEntryPageEditTitle;
}
}
}
public bool EnableSaveButton
{
get { return this._enableSaveButton; }
set { if (this._enableSaveButton != value) this.SetProperty(ref this._enableSaveButton, value); }
}
}
Here is part of my XAML:
<Grid x:Name="LayoutRoot" Background="Transparent" DataContext="{StaticResource PersonEntryViewModel}" >
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" DataContext="{Binding CurrentPerson, Mode=TwoWay}">
<Grid Margin="0,0,0,5">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border BorderBrush="{StaticResource PhoneAccentBrush}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
BorderThickness="5"
Background="Transparent"
CornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="Name:"
Grid.Row="0"
Margin="12,0,0,0"/>
<TextBox Text="{Binding Name, Mode=TwoWay}"
Grid.Row="1">
<!--<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding NameTextChanged, Mode=OneWay}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>-->
</TextBox>
<TextBlock Text="Address:"
Grid.Row="2"
Margin="12,0,0,0"/>
<TextBox Text="{Binding Address, Mode=TwoWay}"
AcceptsReturn="True"
Height="200"
VerticalScrollBarVisibility="Visible"
TextWrapping="Wrap"
Grid.Row="3"/>
As you can see, my layoutRoot grid is binded to my ViewModel i.e. PersonEntryViewModel and the grid content panel containing my textboxes required for editing is binded to CurrentPerson.
Is that the correct way to do it? I need to bind the control to the CurrentPerson property which will contain data if the person is being edited and it will contain a new empty PersonModel if a new person is being added.
As it stands, that part is working. When I type some text in my field and click on the next one, it calls set the CurrentPerson relevant property which in turns calls the PersonModel. Click on the save button and I check the CurrentPerson, I can see it has all the various properties set.
As you can see in my PersonEntryViewModel, I've got other properties which are required. For example the EnableSaveButton, which technically should be set to true or false based on the validation of the various properites from the CurrentPerson object but I need this to be checked as the user is typing text in the various textbox and this is where I'm having a problem.
If I enable the following code:
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding NameTextChanged, Mode=OneWay}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
It doesn't get triggered in the PersonEntryViewModel where I really need it as this is where I want to set my EnableSaveButton property but I guess it makes sense as this code is binded to the Name textbox which is turn is binded to the CurrentPerson property which is my PersonModel.
If I move the code from the PersonEntryViewModel to the PersonViewModel
private ICommand _personTextChanged;
public ICommand PersonTextChanged
{
get
{
return this._personTextChanged ?? (this._personTextChanged = new DelegateCommand(PersonTextChangedAction));
}
}
private void PersonTextChangedAction(object actionParameters)
{
if (!string.IsNullOrEmpty(this._name) && this._name.Length > 2)
{
//EnableSaveButton = true;
Console.WriteLine("");
}
}
It gets triggered accordingly but then how do I get this information back to my PersonEntryViewModel which binded to the view where my 2 buttons (i.e. save & cancel) are located and the EnableSaveButton property is responsible for enabling the save button accordingly when set assuming that the Name is valid i.e. set and minlen is match for example.
Is the PersonEntryViewModel and using a CurrentPerson property with the current person being edited or added designed correctly or not and how am I to handle this scenario?
I hope the above makes sense but if I'm not clear about something, let me know and I'll try to clarify it.
Thanks.
PS: I posted another posted related to how to detect text change, but I figured it out but it's obviously not the problem. The problem seems more related to design.
Your design is unclear to me.
If you want to go with the current design itself, I would suggest you do the following thing.
Remove assigning the DataContext for grid in xaml.
In the code behind add:
var dataContext = new PersonEntryViewModel();
this.ContentPanel.DataContext = dataContext.CurrentPerson;
// After creating App bar
this.appBar.DataContext = dataContext;
//Your xaml code will look something like this:
<AppBar x:Name="appBar">
<Button x:Name="saveBtn" IsEnabled={Binding EnableSaveButton} />
<AppBar />