Progress bar's Visibility property not changing during runtime - c#

I want to be able to hide a progress bar once a button is pressed, but it doesn't seem to be working.
This is my progress bar in XAML :
<ProgressBar Grid.Row="1" Grid.Column="1" IsIndeterminate="True" Height="37" Margin="0,10,0,10" Visibility="{Binding ProgressVisibility, Mode=TwoWay}" RenderTransformOrigin="0.5,0.5"/>
In my .xaml.cs I have this dependency property :
public Visibility ProgressVisibility
{
get { return (Visibility)GetValue(ProgressVisibilityProperty); }
set { this.SetValue(ProgressVisibilityProperty, value); }
}
public static readonly DependencyProperty ProgressVisibilityProperty =
DependencyProperty.Register("ProgressVisibility", typeof(Visibility), typeof(RecipeDownloadDialogStatusView), new PropertyMetadata(Visibility.Visible));
And in the view model I have this property :
private Visibility progressVisibility;
public Visibility ProgressVisibility
{
get
{
return this.progressVisibility;
}
set
{
this.progressVisibility = value;
this.OnPropertyChanged("ProgressVisibility");
}
}
When a button is pressed, I call :
ProgressVisibility = Visibility.Hidden;
While debugging :
The line above gets executed but nothing happens.
The setter gets called and so is the OnPropertyChanged
The dependency property gets registred.
I'm not really sure what did I do wrong. Any help will be appreciated.

Based on your question I think one of two things is the problem here:
The DataContext of the ProgressBar is set to your viewmodel but your Button is only updating the property of your parent control / window.
The DataContext of the ProgressBaris set to your parent control / window but your Button is only updating the property of your viewmodel.
If you want ProgressVisibility to be publicly available, change it to
public Visibility ProgressVisibility
{
get => this.viewModel.ProgressVisibility;
set => this.viewModel.ProgressVisibility = value;
}
If ProgressVisibility does not need to be public, just drop the dependency property and work with the implementation in the viewmodel instead.

Related

WPF Bind User Control property not working

I have a user control and im trying to bind one of its properties
User Control Xaml
<UserControl x:Class="pi_browser.Testing.Example"
...
x: Name="LabelControl">
<StackPanel x:Name="RootStackPanel">
<Label Content="{Binding Text, ElementName=LabelControl}"/>
</StackPanel>
</UserControl>
User Control Codebehind
public partial class Example : UserControl
{
public Example()
{
InitializeComponent();
ExampleViewModel vm = new ExampleViewModel(State);
DataContext = vm;
}
public Boolean State
{
get { return (Boolean)this.GetValue(StateProperty); }
set { this.SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State",
typeof(Boolean), typeof(Example), new PropertyMetadata(false));
}
Main Page View Model
class ExampleViewModel
{
public ExampleViewModel(bool v)
{
val = v;
}
bool val;
public string Text { get => val ? "This worked" : "This didnt work"; }
}
Main Window Xaml
<Window x:Class="pi_browser.Testing.Tester" ... >
<Grid>
<local:Example State="True"/>
</Grid>
</Window>
In this example I didn't bind the State variable, I only passed a literal, but ideally I would like to bind to actual values.
State is a boolean, yet you bind to Text. Let us fix one issue by creating a dependency property Text on your User Control. We shall fix the Text issue and not the boolean State issue. Once you fix that, do the same for State.
So to fix Text we need to fix why this fails:
<Label Content="{Binding Text, ElementName=LabelControl}"/>
You set the ElementName to be the UserControl itself, which is what one wants. But then you tell the binding to look for (remember binding is just reflection of an object under the covers) the property Text. The property Text does not exist on that instance/class...but State does. Its obvious to bind to a newly created Text dependency property on the user control to fix the first issue.
Then when you instantiate the control on your main page, you need to then, and only then bind to Text because that property also resides on your viewmodel.
So three things, along with the change mentioned on the UserControl:
Make your ViewModel adhere to INotifyPropertyChanged and make the Text property use the notification mechanism you install.
Make sure that your main page has its DataContext set to a vailid instance of your ViewModel class.
Bind to Text such as <local:Example State="{Binding Text}"/>
Once that is done, the Text value will properly flow towards the UserControl.

How can I bind a WPF checkbox's IsChecked property to a boolean property of an object that is not a window

I found a lot of examples on how to bind the IsChecked property of a WPF checkbox to a boolean property, if both belong to the same Window class. I want to do a different thing:
I have the main window (excerpt):
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private readonly SoundFx _soundFx = new SoundFx();
private void _StartNewGame()
{
_soundFx.GameStarted();
}
}
Then I have the SoundFx class (excerpt):
public class SoundFx : DependencyObject
{
public void GameStarted()
{
if (Enabled)
{
_PlayGameStartedSound();
}
}
public bool Enabled
{
get { return (bool) GetValue(EnabledProperty); }
set { SetValue(EnabledProperty, value); }
}
public static readonly DependencyProperty EnabledProperty =
DependencyProperty.Register("Enabled", typeof(bool),
typeof(SoundFx), new UIPropertyMetadata(false));
}
And I have the XAML (excerpt):
<Grid>
<CheckBox IsChecked="{Binding ElementName=_soundFx, Path=Enabled}" x:Name="checkBoxSoundFx" Content="Sound FX" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom"/>
</Grid>
To be honest, I'm new to WPF and don't know exactly what I'm doing. What I'm trying to achieve is that the value of _soundFx.Enabled be changed when the user clicks on the checkBoxSoundFx element, without using any event handlers like Checked or Unchecked. This should be possible with data binding, shouldn't it?
First you need to create
public SoundFx _soundFx { get; set; }
as public property, because you cannot bind to private field
public MainWindow()
{
InitializeComponent();
_soundFx = new SoundFx();
}
And from xaml you need to bind like:
<CheckBox IsChecked=
"{Binding RelativeSource=
{RelativeSource Mode=FindAncestor,AncestorType=Window},
Path=_soundFx.Enabled}"}"
x:Name="checkBoxSoundFx"
Content="Sound FX"
HorizontalAlignment="Right"
Margin="0,0,10,10"
VerticalAlignment="Bottom"/>
You were close, you need a property to bind to and you need to set the DataContext if you didn't do it:
public partial class MainWindow
{
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
}
private readonly SoundFx _soundFx = new SoundFx();
public SoundFx {get {return _soundFx;}}
private void _StartNewGame()
{
_soundFx.GameStarted();
}
}
You then need to bind to this property (and set the mode to OneWayToSource if you only need to set the property, never update the CheckBox according to the property value):
<Grid>
<CheckBox IsChecked="{Binding Path=SoundFx.Enabled, Mode=OneWayToSource}" x:Name="checkBoxSoundFx" Content="Sound FX" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom"/>
</Grid>
By the way I'm not sure why you SoundFx is a DependencyObject and why your Enabled property is a DependencyProperty. A simple property would work aswell in this particular example.
DependencyProperties are useful when you want to set them in a Style or animate them with a Storyboard for example, you don't seem to be in this case. I think SoundFx should inherit DependencyObject and Enabled should be a simple property (This is an opinion I make knowing very little about your project though).
As I've managed to grow more experienced in WPF in the meantime, I would now say that my question itself was wrong. In order to avoid confusion in binding and unnecessary dependencies between view and model, I would now always prefer MVVM for cases like this.
Example: https://codereview.stackexchange.com/questions/124361/mvvm-am-i-doing-it-right

StackPanel visibility not updated when dependency property changed

I'm currently developping an universal app in C#/XAML with MVVM (not MVVM Light) and I have trouble for the XAML part.
I'd like to display one or another StackPanel when a dependency property changed in my ViewModel. I think the code speaks for itself.
<StackPanel Visibility="{Binding MyProperty, Converter={StaticResource BooleanToVisibilityConverter}}">
<!-- Some content -->
</StackPanel>
<StackPanel Visibility="{Binding MyProperty, Converter={StaticResource InvertBooleanToVisibilityConverter}}">
<!-- Some another content -->
</StackPanel>
And here is the definition of the dependency property.
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(
"MyProperty",
typeof (bool),
typeof (MyViewModel),
new PropertyMetadata(true));
public bool MyProperty
{
get { return (bool) GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); OnPropertyChanged(); // Implemented by ReSharper }
}
I guess you figure it out that MyProperty is a boolean that I convert into a Visibility via the converters. So, when MyProperty changed in the ViewModel, the view isn't updated.
I already tried to use the UpdateSourceTrigger property but it's not working. Also, I have no binding error and converters are working fine (I only see one StackPanel at the app launch).
Please keep in mind that I don't want to use the code behind part unless there is no other solution.
Thanks for your help.
I finaly gave up and used the code behind part and it's working fine now.
Are your <StackPanel>s part of some UserControl? If not, why are you using DependencyProperty?
Your implementation is quite off as well.
Lets assume for a minute that this is not part of a Custom Control (correct me -- if I'm wrong, I will rewrite the solution)
So you have a ViewModel and you want to hook up some Properties to it. You really don't need to implement DependencyProperty to do what you want to do, but I will entertain you by implementing it your way.
This is a sample ViewModel with 1 (one) property
using Windows.UI.Xaml;
using System.ComponentModel;
// very simple view model
class MyViewModel : DependencyObject, INotifyPropertyChanged
{
// implement INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// register
public static DependencyProperty FooterTitleProperty = DependencyProperty.Register("FooterTitle", typeof(string), typeof(MyViewModel),
new PropertyMetadata(string.Empty, OnFooterTitlePropertyChanged));
// the actual property
public string FooterTitle
{
get { return (string) GetValue(FooterTitleProperty); }
set
{
SetValue(FooterTitleProperty, value);
}
}
// this will fire when the property gets change
// it will call the OnPropertyChanged to notify the UI element to update its layout
private static void OnFooterTitlePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
MyViewModel mvm = dependencyObject as MyViewModel;
mvm.OnPropertyChanged("FooterTitle");
}
}
To test out the code we will make a very simple XAML form
<Grid x:Name="ContentPanel">
<StackPanel>
<TextBlock x:Name="tb" Text="{Binding FooterTitle}" FontSize="48"></TextBlock>
<Button Content="Test Property" Click="Button_Click_1"></Button>
</StackPanel>
</Grid>
When you click on the button we will change the Textbox's Text
public sealed partial class MainPage : Page
{
// create the view model
MyViewModel vm = new MyViewModel();
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
// set the text we initial want to display
vm.FooterTitle = "default text";
// set the DataContext of the textbox to the ViewModel
tb.DataContext = vm;
}
// after the button is click we change the TextBox's Text
private void Button_Click_1(object sender, RoutedEventArgs e)
{
// change the text
vm.FooterTitle = "Test Property Has Changed.";
// what happens is the Setter of the Property is called first
// after that happens it launches the `OnFooterTitlePropertyChanged` event
// that we hook up with the Register function.
// `OnFooterTitlePropertyChanged` launches the INotifyPropertyChanged event
// then finally the TextBox will updates it's layout
}
}
At this point you can guess you really don't need the DependencyProperty and say why can't I just launch the INotifyPropertyChanged in the Setter instead? Well you can and it is probably the prefer method.
If all these is part of a UserControl then I can see using a DependencyProperty then in the OnFooterTitlePropertyChanged event you can set the
name_of_textbox.Text = FooterTitle;
I think property name should be given with OnPropertyChanged method, like this;
public bool MyProperty
{
get { return (bool) GetValue(MyPropertyProperty); }
set {
SetValue(MyPropertyProperty, value);
OnPropertyChanged("MyProperty");
}
}
https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.data.inotifypropertychanged.propertychanged

WPF Dependency property binding breaks and always uses the default value

I have a dependency property defined as below. It is defined in xaml.cs of Childusercontrol. It always uses the default value of RGB(255,0,0) ie. Red.
public Color ForeColor
{
get {return (Color)this.GetValue(ForeColorProperty); }
set {this.SetValue(ForeColorProperty, value);}
}
public static readonly DependencyProperty ForeColorProperty = DependencyProperty.Register("ForeColor", typeof(Color), typeof(Childusercontrol), new PropertyMetadata(Color.FromRgb(255,0,0), OnCurrentForeColorPropertyChanged));
private static void OnCurrentForeColorPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
Childusecontrol control = source as Childusecontrol;
Color fcolor= (Color)e.NewValue;
}
The value is passed through xaml from parent usercontrol as
<UC:Childusercontrol ForeColor="{Binding ChildForeColor}"/>
ChildForeColor is a property of type Color in ViewModel of ParentUserControl and is defined as below.
private Color _ChildForeColor;
public Color ChildForeColor
{
get
{
return _ChildForeColor ;
}
set
{
if (_ChildForeColor != value)
{
_ChildForeColor = value;
RaisePropertyChanged(()=> ChildForeColor );
}
}
}
And ChildForeColor property is set as below, in the parentusercontrol's constructor.
The value being passed as constructor parameter is blue.
public Parentusercontrol(System.Drawing.Color ForeColor)
{
ChildForeColor = Color.FromRgb(ForeColor.R, ForeColor.B, ForeColor.G);
}
But, the InitializeComponent(); of Parent control's xaml.cs clears the value of dependency property and hence, only the default value is used.
Do I have to change the definition of the dependency property? How to fix this bug?
This worked perfectly fine for me!
ChildControl
I gave the UserControl a Name in Xaml i.e
<UserControl ... (all normal namespaces)... x:Name="Child">
<Border>
<Border.Background>
<SolidColorBrush Color="{Binding ForeColor, ElementName=child}"/>
</Border.Background>
</Border>
</UserControl>
The property "ForeColor" is a dependency property as you defined it yourself. This control works perfectly on its own too.
ParentControl
I did the same as with ChildControl. i.e. gave it a name.
<UserControl ... (Usual NS)... x:Name="parent">
<Border BorderThickness="2">
<local:ChildUserControl Margin="5" ForeColor="{Binding ChildForeColor, ElementName=parent}"/>
<Border.BorderBrush>
<SolidColorBrush Color="{Binding ChildForeColor, ElementName=parent}"/>
</Border.BorderBrush>
</Border>
</UserControl>
This also works fine with testing the C# Class looks as follows
public ParentUserControl(System.Drawing.Color c)
{
InitializeComponent();
Color c2 = Color.FromRgb(c.R, c.G, c.B);
ChildForeColor = c2;
}
private Color _ChildForeColor = Color.FromRgb(0, 255, 0);
public Color ChildForeColor
{
get { return _ChildForeColor; }
set
{
if (value != _ChildForeColor)
{
_ChildForeColor = value;
OnPropertyChanged(() => ChildForeColor);
}
}
}
I have assigned the _ChildForeColor a value just for testing, but this is not needed. Please note however that if you run a NotifyPropertyChanged event this cannot happen before InitializeComponent(); This I guess is because nothing yet has been initialized to listen to the change. Therefore you have 2 options. Remove OnPropertyChanged and assign color before InitializeComponent, or use OnPropertyChanged but only assign color after InitializeComponent. The first solution will still work because the property value is changed before the components go and look for the value.
Window for using constructing the controls
This is a bit more tricky as you have assigned a constructor that takes a variable. So my code looks as follows:
public Control ParContent
{
get { return (ContentControl)GetValue(ParContentProperty); }
set { SetValue(ParContentProperty, value); }
}
//Register Dependency ParContent Property
public static readonly DependencyProperty ParContentProperty = DependencyProperty.Register("ParContent", typeof(ContentControl), typeof(MainWindow), new PropertyMetadata( ));
public MainWindow()
{
InitializeComponent();
ParContent = new ParentUserControl(System.Drawing.Color.Blue);
}
and in Xaml
<Window ...Title="MainWindow" Height="478.784" Width="736.87" x:Name="win">
<Grid>
<local:ChildUserControl HorizontalAlignment="Left" Height="100" Margin="122,298,0,0" VerticalAlignment="Top" Width="100"/>
<ContentControl x:Name="Parent" Content="{Binding ParContent,ElementName=win}" HorizontalAlignment="Left" Margin="106,49,0,0" VerticalAlignment="Top" Height="79" Width="93"/>
</Grid>
</Window>
As I said this worked perfectly fine by me and all the properties keep their values.
Possible solutions:
Make sure the parent's childForeColor has a color assigned to it especially when using ordinary properties.
If you use ordinary properties in Parent control make sure INotifyPropertyChange is called if the color is changed after Initialize (Which I guess you subscribe to already)
perhaps use FrameworkPropertyMetadata instead and then add flag AffectsRender - don't think this is the problem, but worth a shot
Play around with the Binding Mode - although I do not think this is the real issue either
If you are working with 2 x controls where 1 property is most likely going to inherit from another use Inherited properties rather - http://msdn.microsoft.com/en-us/library/ms753197(v=vs.110).aspx
Bottom line I have a suspicion that the Parent's "ChildForeColor" might be causing the problem as the above seems ok to me at first glance.
EDIT
Try doing the following. In xaml give your parent control a name x:name="Parent" then in the binding mode do this
<UC:Childusercontrol ForeColor="{Binding ChildForeColor, ElementName="Parent"}"/>
This should sort out any binding issues if the problem lies with the binding.
However you say "Parent control's xaml.cs clears the value of dependency property and hence, only the default value is used." Which indicates that the problem is not with binding or with the child control as far as I can gather...
I also assumed you have stepped through the code so after you hit this
ChildForeColor = Color.FromRgb(ForeColor.R, ForeColor.B, ForeColor.G);
ChildForeColor appears correct and then if you override OnInitialized() and evaluate the value of ChildForeColor after base.OnInitialized(e); has run the ForeColor is still unchanged?
With this I also assume you have not removed InitializeComponent(); from the constructor, and InitializeComponent(); comes after ChildForeColor = ....! In your constructor you do not show where InitializeComponent() is and I assumed it was just for easy reading purpose.
If ForeColor remained unchanged at this point and assuming base.OnInitialized is the first method that runs in OnInitialized. Then Initialization is not the problem, then the alternative suggestion is to change ChildForeColor to a proper dependency property:
public Color ChildForeColor
{
get { return (Color)GetValue(ChildForeColorProperty); }
set { SetValue(ChildForeColorProperty, value); }
}
//Register Dependency ChildForeColor Property
public static readonly DependencyProperty ChildForeColorProperty = DependencyProperty.Register("ChildForeColor", typeof(Color), typeof(ParentControl), new FrameworkPropertyMetadata());
and see if that changes it.

Binding from DependencyProperty of a UserControl not working

I have a UserControl with one DependencyProperty which sets in codebehind (I guess this may be a source of my problem, but still don't know what to do):
UserControl
public partial class MyControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyControl),
new FrameworkPropertyMetadata("",FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); InvokePropertyChanged(new PropertyChangedEventArgs("Text"));}
}
public static string GetText(DependencyObject obj)
{
return (string)obj.GetValue(TextProperty);
}
public static void SetText(DependencyObject obj, string value)
{
obj.SetValue(TextProperty, value);
}
private void ChangeText()
{
Text="some value";
}
}
In my View.xaml I use this control like this:
<MyControl Text="{Binding Text, RelativeSource={RelativeSource Self}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
And the Text property in my ViewModel:
private string _text;
public string Text
{
get { return _text; }
set { _text= value; InvokePropertyChanged(new PropertyChangedEventArgs("Text"));}
}
The problem:
Text property in the ViewModel never gets updated; when use binding with a regular control like TextBox, all works perfect; if I set Text in XAML, Text propery of UserControl updates.
What I did wrong?
UPDATE
My issue was that I have set DataContext explicitly on MyControl.
Issue is in your Binding:
Text="{Binding Text, RelativeSource={RelativeSource Self},
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Text property is in your ViewModel but you are referring to itself by using RealtiveSource to point back to self. So, it's binding Text DP with itself.
If you have set DataContext of your control, it will automatically inherit DataContext from parent. So, you don't need RelativeSource at all.
It simply should be:
Text="{Binding Text}"
Few points more (but not related to your issue):
Since you target to use this property from within control, so go for normal DP instead of attached property.
Since at time of registration, you have set it to bind TwoWay by default. No need to explicitly do that at time of binding.
Remove InvokePropertyChanged call from your DP wrapper setter. Setter won't be called from XAML and also DP is already PropertyChanged aware.
UPDATE
In case DataContext of MyControl is set to instance of another class, above approach will search for Text property in MyControl DataContext.
You can pass DataContext of parent control (StackPanel in your case) like this:
Text="{Binding DataContext.Text, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=StackPanel}}"
You have registered your property as attached, yet you are also using it as a regular DependencyProperty. I think that the xaml parser gets confused. Decide which one you want to use.

Categories

Resources