In my project I am setting a bool property as true when constructing my ViewModel.
In the View, I have a RadioButton which is bound to the value of this property. The first time I open the View, the RadioButton is "checked" (perfect, exactly what I want!).
However, if I close and then re-open the View, the RadioButton is not "checked", despite the bound property having a value of 'true'. In my constructor I am setting 'StaggeredMode' to true, but the setter is being called three times (firstly, value = true; secondly, value = false; thirdly, value = true)
Any help will be appreciated!
XAML:
<RadioButton GroupName="AppointmentStart"
Content="Staggered"
IsChecked="{Binding StaggeredMode, Mode=TwoWay}"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="310,247,0,0" />
C#:
private bool staggeredMode;
public bool StaggeredMode
{
get { return staggeredMode; }
set { staggeredMode = value; }
}
Your StaggeredMode property should be observable so the view can be notified about property changes from your ViewModel.
You can implement INotifyPropertyChanged by yourself or use one of existing implementations like this, this or this.
Here is a basic implementation of INotifyPropertyChanged interface:
public abstract class ObservableObject : INotifyPropertyChanged
{
public virtual void RaisePropertyChanged(string propertyName)
{
OnPropertyChanged(propertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
Then you derive your ViewModel from ObservableObject and raise OnPropertyChanged event every time your property value has been changed:
public class MyViewModel : ObservableObject
{
...
private bool staggeredMode;
public bool StaggeredMode
{
get { return staggeredMode; }
set
{
staggeredMode = value;
OnPropertyChanged("StaggeredMode");
}
}
...
}
Related
I have a background-thread which updates a Integer-value once per second. How can I map this Integer to a Text-Field of my XAML form that the Form always shows the current value and updates automatically if the Integer changes?
You can use a ViewModel with a binding.
With OnPropertyChanged() it will automatically changed and displayed in your UI.
here is an example to give you an idea
use in your xaml:
<TextBox x:Name="MyTextBox" Text="{Binding Name}".../>
in your code behind:
...
var vm = new ViewModel("Nr.7");
this.BindingContext = vm;
foreach(var x in Whatever)
{
vm.Name = x;
}
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MyAppNamespace
{
// This class implements INotifyPropertyChanged
// to support one-way and two-way bindings
// (such that the UI element updates when the source
// has been changed dynamically)
public class ViewModel : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel()
{
}
public ViewModel(string value)
{
this.name = value;
}
public string Name
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged();
}
}
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
I am very new to the MVVM style. I have been able to accomplish everything in the past by writing extensive code in the code behind the XAML. However, I am trying to convert everything into MVVM.
Issue:
I am unable to bind a datagrid to my ObservableCollection. When I debug and walk through my code, the ObservableCollection list is being properly set, meaning, i can view the data within the variables and it shows the data i am adding; however, my data grid does not display the data. Upon reading I found that if I used an ObservableCollection, I didn't need an INotifyPropertyChanged; but because that didn't work, i do have one on there now. Sadly, that still isn't populating my datagrid.
Any help you can offer is appreciated.
In the end, I am going to have a list of type Members, and i need the data grid to populate the data about each member. The data comes from the Members class.
Xaml:
<DataGrid ItemsSource="{Binding Source=PHList}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Member Name" Binding="{Binding Member_Name}"/>
<DataGridTextColumn Header="Medicaid ID" Binding="{Binding Medicaid_ID}"/>
</DataGrid.Columns>
</DataGrid>
Priority Health Model:
public PriorityHealthMember(string name, string id)
{
Member_Name = name;
Medicaid_ID = id;
}
private string _Member_Name;
public String Member_Name
{
get
{
return _Member_Name;
}
set
{
_Member_Name = value;
OnPropertyChanged("Member_Name");
}
}
private string _Medicaid_ID;
public String Medicaid_ID
{
get
{
return _Medicaid_ID;
}
set
{
_Medicaid_ID = value;
OnPropertyChanged("Medicaid_ID");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
Onboarding ViewModel
private ObservableCollection<PriorityHealthMember> _PHList;
public ObservableCollection<PriorityHealthMember> PhList
{
get
{
return _PHList;
}
set
{
_PHList = value;
OnPropertyChanged("PhList");
}
}
public OnboardingQueueViewModel()
{
PhList = GetOnboardingQueueList();
}
private ObservableCollection<PriorityHealthMember> GetOnboardingQueueList()
{
ObservableCollection<PriorityHealthMember> list = new ObservableCollection<PriorityHealthMember>();
list.Add(new PriorityHealthMember("Andrews, Nicholas", "M123456"));//Testing for now. Will add the Business Logic after binding works.
return list;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
I found my issue. In my xaml i had "PHList" however, my property was "PhList"
We have used DataGridTemplateColumn for our grid to display texbox under each column. We've a requirement to make the textboxes readonly if it contains any data (for data loading case). In order to achieve that, we need to access all text box controls under the radgrid. We've tried following approaches so far
Find all child controls using VisualTreeHelper - No textbox control was found
Tried with DataBindingComplete event
Is there any way to access the underlying cell's control from codebehind for RadDataGrid?
Alternative approach : Can we somehow user IsReadOnly property with some binding to check it's value and make the control readonly when value is there?
Can we somehow user IsReadOnly property with some binding to check it's value and make the control readonly when value is there?
Yes. You could certainly achieve this by using Binding. You just need to define a bool property and bind the IsReadOnly property of TextBox to this property. Then, you could change this bool value according to the text of TextBox.
Please refer to my following code sample for reference:
<telerikGrid:RadDataGrid x:Name="grid" AutoGenerateColumns="False" VerticalAlignment="Center">
<telerikGrid:RadDataGrid.Columns>
<telerikGrid:DataGridTemplateColumn Header="Country">
<telerikGrid:DataGridTemplateColumn.CellContentTemplate>
<DataTemplate>
<TextBox Text="{Binding Country}" HorizontalAlignment="Center" VerticalAlignment="Center" IsReadOnly="{Binding IsReadOnly}"/>
</DataTemplate>
</telerikGrid:DataGridTemplateColumn.CellContentTemplate>
</telerikGrid:DataGridTemplateColumn>
<telerikGrid:DataGridTemplateColumn Header="Flag">
<telerikGrid:DataGridTemplateColumn.CellContentTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Flag}" />
</StackPanel>
</DataTemplate>
</telerikGrid:DataGridTemplateColumn.CellContentTemplate>
</telerikGrid:DataGridTemplateColumn>
</telerikGrid:RadDataGrid.Columns>
</telerikGrid:RadDataGrid>
public sealed partial class MainPage : Page
{
ObservableCollection<Data> list = new ObservableCollection<Data>();
public MainPage()
{
this.InitializeComponent();
list.Add(new Data { Country = "Argentina",Flag="A"});
list.Add(new Data {Country=string.Empty,Flag="B"});
list.Add(new Data { Country = "China",Flag="C"});
this.grid.ItemsSource = list;
this.Loaded += MainPage_Loaded;
}
private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
await Task.Delay(5000);
list[1].Country = "Brazil";
}
}
public class Data:INotifyPropertyChanged
{
private string _Country;
public string Country
{
get { return _Country; }
set
{
_Country = value;
if (string.IsNullOrEmpty(value))
{
IsReadOnly = true;
}
else
{
IsReadOnly = false;
}
RaisePropertyChanged("Country");
}
}
private string _Flag;
public string Flag
{
get { return _Flag;}
set
{
_Flag = value;
RaisePropertyChanged("Flag");
}
}
private bool _IsReadOnly=false;
public bool IsReadOnly
{
get { return _IsReadOnly; }
set
{
_IsReadOnly = value;
RaisePropertyChanged("IsReadOnly");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(PropertyName));
}
}
}
I am binding a Datagrid to Observablecollection.Below is my ObservableCollection class.
But the Property changed is always NULL and it is null even after Making my XAML like this.
Please Guide me in this
Thanks!
<DataGridTextColumn Binding="{Binding, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged} Header = "Serial" />
public class itemobject
{
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged( String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
int sno1;
public int Sno
{
get
{ return sno1; }
set
{
if (value != sno1)
{
sno1= value;
NotifyPropertyChanged("Sno");
}
}
}
In XAML you should specify which property exactly changed.
<DataGridTextColumn Binding="{Binding Path = Sno} Header = "Serial" />
And like i did, you need to create ViewModelBase class.
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable {
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string propertyName = null) {
var handle = PropertyChanged;
handle?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public virtual void Dispose() => PropertyChanged = null;
}
public class itemobject : ViewModelBase{
int sno1;
public int Sno{
get => sno1;
set{
if (value != sno1){
sno1= value;
OnPropertyChanged(nameof(Sno));
}
}
}
I've been trying my hand at creating a simple user control with dependencyproperty and binding it, but it doesn't seem to work, not sure why. I'll just dive straight into code, please ignore the fact that the control doesn't make sense, it's just for illustrative purposes (written in WP8 if that matters).
My simple user control, it's basically a line with a property to turn it off or on.
<Grid x:Name="LayoutRoot" Background="Transparent">
<Line Height="105" Width="105" X2="100" Y2="100" Visibility="{Binding LineVisible}" Stroke="#FFFC1515" StrokeThickness="5"/>
</Grid>
public partial class SimpleUserControl : UserControl
{
public SimpleUserControl()
{
InitializeComponent();
DataContext = this;
}
public static readonly DependencyProperty LineVisibleProperty = DependencyProperty.Register("LineVisible", typeof(bool), typeof(SimpleUserControl), new PropertyMetadata(new PropertyChangedCallback(OnLineVisibleChanged)));
public bool LineVisible
{
get { return (bool)GetValue(LineVisibleProperty); }
set { SetValue(LineVisibleProperty, value); }
}
private static void OnLineVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
bool newvalue = (bool)e.NewValue;
Visibility vis = newvalue ? Visibility.Visible : Visibility.Collapsed;
(d as SimpleUserControl).Visibility = vis;
}
}
The test app
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<uc:SimpleUserControl LineVisible="{Binding class1.Vis}"/>
</Grid>
public partial class MainPage : PhoneApplicationPage
{
public Class1 class1 { get; set; }
public MainPage()
{
InitializeComponent();
DataContext = this;
}
private void PhoneApplicationPage_Loaded_1(object sender, RoutedEventArgs e)
{
class1 = new Class1() { Vis = false };
}
}
The class1 that it's bound to
public class Class1 : INotifyPropertyChanged
{
private bool _vis;
public bool Vis
{
get { return _vis; }
set
{
_vis = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Vis"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
It doesn't seem to work, however, if it is set explicitly like below it works.
<uc:SimpleUserControl LineVisible="False"/>
I'm sure it's something simple, but I'm not seeing it.
Thanks for any help.
The problem was I was setting the DataContext = this in the UserControl and when binding to Vis in the testapp, it would override and search for Vis in the UserControl (which of course does not exist there). I did see binding errors in the debug output window which confirms this. The solution was to set the LayoutRoot of the UserControl to this as was mentioned in the link I posted earlier.
The Visibility property of WPF controls does not use bool values, it requires the Visibility enum. Thus you have two options:
Change LineVisibiltyProperty to Visibility instead of bool.
Use a converter to bind to bool and convert to Visibility.
I would suggest using the second option as this in my opinion is the better solution.
This might be helpful.