I have an usercontrol in a UWP who I place in other user controls who have two text TextBlocks who are bound to the VM.
Here is the XAML Code:
DataContext
DataContext="{Binding BalanceView, Source={StaticResource CoreModule}}"
<TextBlock Text="{Binding TotalBalance, Mode=TwoWay, Converter={StaticResource AmountFormatConverter}, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource DeemphasizedBodyTextBlockStyle}"
Margin="0,0,5,0" />
<TextBlock Text=" / "
Style="{StaticResource DeemphasizedBodyTextBlockStyle}"
Margin="0,0,5,0" />
<TextBlock Text="{Binding EndOfMonthBalance, Mode=TwoWay, Converter={StaticResource AmountFormatConverter}, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource DeemphasizedBodyTextBlockStyle}"
Margin="0,0,5,0" />
And the VM Properties there bound to:
public double TotalBalance
{
get { return totalBalance; }
set
{
if (Math.Abs(totalBalance - value) < 0.01) return;
totalBalance = value;
RaisePropertyChanged();
}
}
public double EndOfMonthBalance
{
get { return endOfMonthBalance; }
set
{
if (Math.Abs(endOfMonthBalance - value) < 0.01) return;
endOfMonthBalance = value;
RaisePropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
I can see that the value returned is correct. But on the UI it's permanently on 0. If I set the value staticly to a value it's shown properly.
What is wrong?
I would suspect you have these double values as private properties which are not shown here, but are referenced such as "totalBalance" vs PUBLIC "TotalBalance" and similar with "endOfMonthBalance" vs "EndOfMonthBalance", otherwise it would not compile.
Also, shouldn't your RaisePropertyChanged() call
RaisePropertyChanged("TotalBalance") and RaisePropertyChanged("EndOfMonthBalance") vs a blanket call via no parameter.
Call OnPropertyChanged passing the name of the CLR property as the paramter.
private double totalBalance=0;
public double TotalBalance
{
get { return totalBalance; }
set
{
if (Math.Abs(totalBalance - value) < 0.01) return;
totalBalance = value;
OnPropertyChanged("TotalBalance");
}
}
private double endOfMonthBalance=0;
public double EndOfMonthBalance
{
get { return endOfMonthBalance; }
set
{
if (Math.Abs(endOfMonthBalance - value) < 0.01) return;
endOfMonthBalance = value;
OnPropertyChanged("EndOfMonthBalance");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
Related
I have 3 checkboxes. Lets call them cb1,cb2 and cb3. The cb3 should be checkëd when cb1 and cb2 are checked. How do I Implement this in WPF.? I am new to WPF.
Thanks in advance.
If you are using a ViewModel, just add a new computed property to that.
A simple view model would look like:
public class MyVieWModel : INotifyPropertyChanged
{
private bool _cb1Checked;
private bool _cb2Checked;
public bool CB1Checked
{
get { return _cb1Checked; }
set
{
_cb1Checked = value;
PropertyChanged(this, new PropertyChangedEventArgs("CB1Checked"));
PropertyChanged(this, new PropertyChangedEventArgs("CB3Checked"));
}
}
public bool CB2Checked
{
get { return _cb2Checked; }
set
{
_cb2Checked = value;
PropertyChanged(this, new PropertyChangedEventArgs("CB2Checked"));
PropertyChanged(this, new PropertyChangedEventArgs("CB3Checked"));
}
}
public bool CB3Checked
{
get { return _cb1Checked && _cb2Checked; }
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
After setting CB1Checked or CB2Checked, you need to raise the event that CB3Checked has also changed.
Your XAML would look something like (and this is from memory...):
<CheckBox IsChecked={Binding CB1Checked}" />
<CheckBox IsChecked={Binding CB2Checked}" />
<CheckBox IsChecked={Binding CB3Checked, Mode=OneWay}" />
As #wkl points out in the comments, the third checkbox should be a one-way binding since the value can't be set.
Some MVVM frameworks might make this a little easier for you, I've not used any to be able to recommend though.
You can do it like this for a single checkbox:
<CheckBox x:Name="cb3" IsChecked="{Binding Path=IsChecked, ElementName=cb2}" />
I assume the other checkbox is called cb2.
For multiple checkboxes I recommend a Binding in you DataContext.
<CheckBox IsChecked="{Binding Path=CB_1_Checked}" Content="CheckBox 1" />
public bool CB_1_Checked
{
get { return _cb_1_checked; }
set
{
_cb_1_checked = value;
OnPropertyChanged();
//Notify that CB_3_Checked may have changed:
OnPropertyChanged("CB_3_Checked");
}
}
Do this for CB1 and CB2.
Add this for CB3
//Will return 'true' when both are checked (but lacks OnPropertyChanged !)
public bool CB_3_Checked => (CB_1_Checked && CB_2_Checked);
Or with a little more options:
private bool _cb_3_checked;
public bool CB_3_Checked
{
get
{
if(CB_1_Checked && CB_2_Checked)
{
_cb_3_checked = true;
}
return _cb_3_checked;
}
set
{
_cb_3_checked = value;
OnPropertyChanged();
}
}
Read more about Bindings here.
I have a following label and a slider in my UserControl xaml
<Label x:Name="labelValX" Content="{Binding Path=xValue}" HorizontalAlignment="Left" Width="88" Height="44"/>
<Slider x:Name="sliderSpeed" Value="{Binding slideValue, Mode=TwoWay}" HorizontalAlignment="Left" Margin="10,35,0,0" VerticalAlignment="Top" Width="173" Height="53" Minimum="10" Maximum="100" />
and a specific SetGetAccValues.cs class:
public class SetGetAccValues : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _xval;
public string xValue
{
get { return _xval; }
set
{
if (value != _xval)
{
_xval = value;
OnPropertyChanged("xValue");
}
}
}
public byte _slideValue;
public byte slideValue {
get
{
return _slideValue;
}
set
{
if (value != _slideValue)
{
_slideValue = value;
OnPropertyChanged("slideValue");
}
}
}
protected virtual void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
if (propName.Equals("slideValue"))
{
speedAccMeter(slideValue);
}
}
}
An in my other GetAccNotifications.cs class, I have following part, where I'm defining my xValue string to a specific value:
Y = ((double)(sbyte)value) / 64.0;
Y = Math.Round(Y, 2);
SetGetAccValues set = new SetGetAccValues();
set.xValue = Y.ToString();
The problem occurs when the OnPropertyChanged is triggered with "xValue" as the propName, the PropertyChangedEventHandler remains always null, but when it is triggered with "slideValue" as propName it is actually not null. Why does it remain null in the xValue case ?.
I believe the PropertyChanged event is firing before the datacontext is finished loading.
You can listen to the DataContextChanged event in your usercontrol, so that when the new datacontext is available, you can set your properties.
public AccView()
{
InitializeComponent();
this.DataContextChanged += OnDataContextChanged;
this.DataContext = new SetGetAccValues();
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
Y = ((double)(sbyte)value) / 64.0;
Y = Math.Round(Y, 2);
(dependencyPropertyChangedEventArgs.NewValue as SetGetAccValues).xValue = Y.ToString();
}
I think you didn't bind DataContext. You should set DataContext with code behind in your case.
In SetGetAccValues.xaml.cs
public SetGetAccValues()
{
DataContext = this;
}
I have a LineSeries to which I am binding the data from an ObservableCollection of the type ChartData. Now, In my UI, I have a TextBox in which I need to show the Y value of the series. How do I bind the Value property to the TextBox
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ObservableCollection<ChartData> lineSeries1Data = new ObservableCollection<ChartData>();
simChart.DataContext = lineSeries1Data;
}
public class ChartData : INotifyPropertyChanged
{
DateTime _Name;
double _Value;
public DateTime Name
{
get
{
return _Name;
}
set
{
_Name = value;
OnPropertyChanged("Name");
}
}
public double Value
{
get
{
return _Value;
}
set
{
_Value = value;
OnPropertyChanged("Value");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
This is my XAML for the TextBox:
<TextBox Name="TxtSignal1Vh1" DataContext="lineSeries1Data" Text="{Binding ChartData.Value}" />
Here is some Working Code,Create a Usercontrol consisting of Chart+textboxes and then binds its Datacontext to respective observable collection.
public MainWindow()
{
InitializeComponent();
observableCollection<DataChart>1st=new observableCollection<DataChart>();
observableCollection<DataChart>2nd=new observableCollection<DataChart>();
win.DataContext = (1st Observable Collection)
lose.DataContext=(2nd Observable Collection)
}
MainWindows xaml
<Grid>
<this:UserControl1 x:Name="win" Margin="10,21,325,144"/>
<this:UserControl1 x:Name="lose" Margin="275,21,10,144"/>
</Grid>
UserControl xaml
<Grid>
<TextBox Text="{Binding Path=Value}" VerticalAlignment="Top" Width="164"/>
<TextBox Text="{Binding Path=Name}" VerticalAlignment="Top" Width="164"/>
</Grid>
the TextBlock binding does not work and I cant figure why...
(This Code Works but the TextBlock does not get Updated )
XAML
<TextBlock x:Name="filterAllText"
Text="{Binding UpdateSourceTrigger=PropertyChanged}" />
Codebehind
filterAllText.DataContext = LogSession.test.MyCoynt;
C#
public class Test : INotifyPropertyChanged {
public int myCoynt;
public int MyCoynt {
get { return myCoynt; }
set {
myCoynt = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(
[CallerMemberName] String propertyName = "") {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Try this:
<TextBlock x:Name="filterAllText"
Text="{Binding UpdateSourceTrigger=PropertyChanged, Path=MyCoynt}" />
And set your DataContext like:
filterAllText.DataContext = LogSession.test;
<TextBlock x:Name="filterAllText" Text="{Binding Path=., UpdateSourceTrigger=PropertyChanged}" />
this should work but its not the usual way
EDIT: the better way is the anwser from Goanne
just need a little help with some databinding.
So I have an ObservableCollection of a custom object. The properties of the object are as shown:
/// <summary>
/// Name
/// </summary>
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged("Name");
}
}
}
/// <summary>
/// Status
/// </summary>
private int _status;
public int Status
{
get
{
return _status;
}
set
{
if (value != _status)
{
_status = value;
NotifyPropertyChanged("Status");
}
}
}
/// <summary>
/// Visible information
/// </summary>
private Visibility _visible;
public Visibility Visible
{
get
{
return _visible;
}
set
{
if (value != _visible)
{
_visible = value;
NotifyPropertyChanged("Visible");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And basically I have three TextBlocks, the first two of which have Text binded to Name and Status while the third has:
Visibility = {Binding Visible}
But whenever I want to toggle the visibility property, I can only toggle it from the Visible state to Collapsed, and then not again. My toggle looks like this:
ItemViewModel l = ((sender as LongListSelector).SelectedItem) as ItemViewModel;
MessageBox.Show(l.Visible.ToString());
if (l.Visible == Visibility.Collapsed)
l.Visible = Visibility.Visible;
else
l.Visible = Visibility.Collapsed;
Note: ItemViewModel is the custom class for the collection, ie.
ObservableCollection<ItemViewModel>
Obviously it's just a rough test, but still doesn't work. The messagebox is always showing "Visible" regardless of the real visibility state of the item. How can I get it to get the real value of Visibility?
If anyone has any idea, that would be great.
Thanks.
EDIT: Adding XAML for the Textblocks for clarity:
<TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}" FontSize="20"/>
<TextBlock Text="{Binding Status}" TextWrapping="Wrap" Margin="12,-6,280,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
<TextBlock x:Name="t1" Text="Test for visibility" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}" Visibility="{Binding Visible}"/>
Keep in mind these textblocks are encased within a stackpanel and a longlistselector so simply changing t1.Visible is out of the question.
The problem, after more investigation, ends up being that the property was being set by another branch of code.