Accessing inner controls of UserControl - c#

I am building a custom UserControl which would allow me to place text inside a ProgressBar. The problem is, none of the ProgressBar's DependencyProperties get transferred over. Here is the XAML for my custom UserControl.
<UserControl
x:Class = "MyProgram.MyCustomProgressBar"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" >
<Grid>
<ProgressBar Name="uiProgressBar" />
<Label Name="uiLabel" />
</Grid>
</UserControl>
How can I access and set the Minimum, Maximum, Value, etc. from XAML when I start using this UseControl.
<local:MyCustomProgressBar x:Name="uiLoadTime" ??.uiProgressBar.Maximum="50" />
I am hoping I don't need to redefine a bunch of DependencyProperties in order to get this functionality.

The usual way is to use a DependencyProperty... it's not so bad once you got used to it really.
Use the "propdp" built in snippet in the code-behind of your usercontrol.
Let's take the ProgressBar.Maximum example:
Make an integer dependencyproperty with a default value of 100 (or whatever you like), name it InnerProgressBarMax.
In your UserControl's XAML, you bind it this way:
<ProgressBar Maximum="{Binding InnerProgressBarMax, ElementName=myUsrCtrl}" />
When you use the control in another part of your application, simply enter a value like this:
<local:MyCustomProgressBar x:Name="uiLoadTime" InnerProgressBarMax="50" />
Rinse & repeat for each property you want to expose.
Edit:
If you really need to have 50+ DP exposed, you could bring down the hastle by specifying smart default values for your DPs.
To do that, when you create a DP, set the parameter of new PropertyMetadata(YOUR_DEFAULT_VALUE)
Once that is done, your control may expose many DPs, but you'll only have to set a few manually in the XAML code that uses the control.

The alternative to wrapping everything in DependencyProperties is to let the UserControl's consumer provide the ProgressBar. It could look like this:
<local:MyCustomProgressBar x:Name="uiLoadTime">
<local:MyCustomProgressBar.ProgressBar>
<ProgressBar Maximum="50" />
</local:MyCustomProgressBar.ProgressBar>
</local:MyCustomProgressBar>
Then in MyCustomProgressBar.xaml.cs, you would expose a public property for a ProgressBar. In its setter, modify the properties however you see fit, then add it to the UserControl's root Grid.

Related

How to access properties that are in an Inherited UserControl in C#?

I created an UserControl that consists of a TextBox and a Label controls. I created another UserContol that inherits the first, on which I added a Button. The difficult that I’m experiencing is that I can’t access directly from secondary UserControl properties like TextBox.Text that are in the primary UserControl. Is there a easy way to access properties from the primary UserControl or the only way is to create a public methods, like a “GetText()”, where I return the TextBox.Text property?
I assume you are talking about WPF.
Xaml view you can use x:FieldModifier="Public"
<Button x:Name="MyButton" x:FieldModifier="Public" />
Note that accessing inner controls is not best practise, your code should not care what controls view contains. If you want for example make button disabled create property in code behind and bind buttons IsEnabled to that property instead

Data binding to custom UserControl

So, I have made my own subclass of UserControl, called ChildView (I really can't come up with a decent name), that I want to show inside a container in a window, I have many different kinds of these UserControls and the window must be capable of showing all of them. The UserControls have implemented my subclass like this:
<src:ChildView x:Class="(namespace).LoginView" [...]>
public partial class LoginView : ChildView
And I have tried to add it to my window like so:
<Grid x:Name="ViewHolder" Grid.Column="1" Grid.Row="1">
<src:ChildView DataContext="{Binding CurrentView}" />
</Grid>
private ChildView _currentView;
public ChildView CurrentView
{
get { return _currentView; }
set
{
if (_currentView == value)
return;
_currentView = value;
smLog.Trace("View set to {0}", value.GetType().Name);
NotifyPropertyChanged("CurrentView");
}
}
However, this does not work. Nothing is shown in my container when I set CurrentView. There are no error messages in the output that would indicate a problem with the binding. Other data bindings in the window works. I can use my ChildViews by specifying their classes directly in the XAML, i.e:
<Grid x:Name="ViewHolder" Grid.Column="1" Grid.Row="1">
<src:LoginView />
</Grid>
I've read some about dependency properties but I don't think I need one here? I did try to implement one anyway but it didn't seem to help, though I probably made some mistake, I couldn't quite wrap my head around it...
So I guess my question is; do I need a dependency property? If so, how do I implement it in this case? If not, what is the problem?
Changing the Child's DataContext won't matter, you're trying to change the control itself, not the data it's bound to. What you need to do is add a placeholder control that would contain the actual view. WPF has such a thing built in, take a look at ContentControl.
Change your grid so it'll containt a ContentControl instead of ChildView, and bind the view to the control's Content property
<Grid>
<ContentControl Content="{Binding CurrentView}"/>
</Grid>

Correct and proper binding of a control/UIElement visibility to Property MVVM C# WPF

I'm quite new to MVVM, and I've been constructing my ViewModels. I have a ViewModel which contains an ICommand, which is then bound to in my View by a command button. The ICommand causes a procedure to be invoked on my ViewModel which then invokes a further large slow procedure. While this procedure is happening I want to make a control/UIElement's visibility to become visible and then hidden after the procedure has finished (I intend to bind a label and indeterminate progress bar's visibility)
For example, in my view model I have
public void calledFromCommandButton() {
RaisePropertyChange("Starting");
superLongProcedure();
RaisePropertyChange("Finished");
}
This just feels a bit silly though, having to raise 2 different property changes and hence, I presume I'm doing it all wrong. I think I could do it with one property change along with a convertor?
So, what is the proper and correct method to bind UIElement visibilities to property change events?
Thanks
Thomas
I would recommend using a single boolean property (IsWorking or something) and then using the BooleanToVisibilityConverter to show and hide the button. So, it would look something like:
<Window ...>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="TrueToVisibleConverter"/>
</Window.Resources>
...
<Button x:Name="CancelButton" Content="Cancel" Visiblity="{Binding IsWorking, Converter={StaticResource TrueToVisibleConverter}}"/>
...
</Window/>

Why does binding to a DepenencyProperty on a user control use the user control's Data Context?

Say I have a WPF application (exe) that has this in the MainWindow.xaml:
<Grid>
<extraControls:MyMVVMUserControl MyDependencyProperty="{Binding Something}"/>
<extraControls:MyUserControl MyDependencyProperty="{Binding Something}" />
</Grid>
and my MainWindow.xaml.cs looks like this:
public MainWindow()
{
DataContext = new MainWindowVM();
InitializeComponent();
}
And my MainWindowVM.cs has a property setup for Something that notifies on property changed.
The user controls are made in a separate dll. As you may guess, MyMVVMUserControl has the DataContext set to a view model.
public MyMVVMUserControl()
{
DataContext = new MyMVVMUserControlVM();
InitializeComponent();
}
MyUserControl does not have a DataContext set in the code behind.
So the interesting thing is that they both have MyDependencyProperty setup exactly the same.
But the MVVM version does not work.
After digging in a bit, I found that the {Binding Something} in MainWindow.xaml is using the View Model setup for the MyMVVMUserControl as the DataContext (rather than the DataContext set in MainWindow.cs (set to MainWindowVM)).
And my question is why?
Why would WPF look inside the user control and use it's DataContext for a binding that is in the actual application?
(NOTE: I know I can get around this by setting the source in the binding, but I want others to be able to use my user controls. But with this issue, I now have a built-in "gotcha" for anyone I want to use my user controls.)
I think I understand you problem, and I'm gonna to give a solution that works for me (I had this problem before). The think is that seams that you are setting the DataContext for you MyMVVMUserControl in code behind, and then it take the bindings from that.
The solution I found for this, is to set the datacontext in code behind, but not at the user control. Set the datacontext for the UserControl's child item. For instance, supose this is the Xaml of your UserControl:
<UserControl ... x:Name="userControl">
<Grid x:Name="rootContainer">
...
</Grid>
</UserControl>
Then in the code behind set the rootContainer's data context, in this way all visual children can access to the control data context, and also the user control datacontext is empty.
...
rootContainer.DataContext = new UserControlViewModel();
...
Hope this may helps you to solve your issues...
You really shouldn't ever set the DataContext of a UserControl from inside the UserControl. By doing so, you are preventing any other DataContext from getting passed to the UserControl, which defeats one of WPF's biggest advantages of having separate UI and data layers.
WPF objects only inherit their DataContext from the parent object if the DataContext is not set to anything else. When your MyMVVMUserControl is being created, you are setting the DataContext to a new MyMVVMUserControlVM, which prevents the DataContext from being inherited from MainWindow.
So its normal that your MVVMUserControl would have it's DataContext set to your MyMVVMUserControlVM, because you set it explicitly in the UserControl's constructor.
This is by-design. UI objects in WPF/MVVM are only meant to be visual representations of the data layer, so it wouldn't make much sense to set the data layer and then try to bind your properties to something that is not on the data layer.
For example, take this line of code:
<UserControl DataContext="{Binding ClassA}" Content="{Binding Name}" />
This would bind the Content property to UserControl.DataContext.Name, which is ClassA.Name. It wouldn't make much sense if this would result in binding to UserControl.Parent.DataContext.Name, as the binding should refer to to the current object's DataContext, and not the Parent's DataContext.
So the only time I ever set the DataContext of a UserControl from inside the UserControl itself is if the UserControl is its own separate object that is never meant to interact with data from the rest of the application. Which so far has been never :)
Typically my UserControls are almost always one of two things:
Either a visual representation of a ViewModel (or Model), such as a CustomerUserControl for a CustomerViewModel, in which case I pass them the DataContext they need when they get used
For example,
<local:CustomerUserControl DataContext="{Binding SelectedCustomer}" />
or
<DataTemplate DataType="{x:Type local:CustomerModel}">
<local:CustomerUserControl />
</DataTemplate>
Or a self-sustained UI object that receives any external data it needs via custom DependencyProperties, and does any additional logic in the code-behind the control, such as a DatePicker control that has a SelectedDate dependency property, or a CalculatorUserControl with dependency properties for Equation and Value
<local:DatePickerUserControl SelectedDate="{Binding SomeDate}" />
<local:CalculatorUserControl Equation="{Binding SomeString}"
Value="{Binding SomeDouble}" />
In your case, it sounds like you should be using the first case, and should be passing a ViewModel into your UserControl containing the data it needs.
<extraControls:MyMVVMUserControl DataContext="{Binding MyMVVMUserControlVM}"
MyDependencyProperty="{Binding Something}">
or
<extraControls:MyMVVMUserControl MyDependencyProperty="{Binding Something}">
<extraControls:MyMVVMUserControl.DataContext>
<viewModels:MyMVVMUserControlVM />
</extraControls:MyMVVMUserControl.DataContext>
<extraControls:MyMVVMUserControl />

Binding in DynamicResource ResourceKey wpf mvvm

I want to bind the style of button on the basis of if else condition. I have created one string property in the viewmodel and bind to the button's style attribute like this:
<Button x:Name="copd" Content="COPD"
Command="{Binding COPDReadingsCommand}"
Style="{DynamicResource ResourceKey={Binding CheckCopd}}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Margin="20" FontWeight="Bold" />
I am looping through the resourceDictionary and getting all the keys. Using if else i am changing the string property value(CheckCopd) in if else.
I am getting the desired values in if else but style is not getting applied to the button when I execute my application. It only displays the generic button style.
How to bind the DynamicResource ?
Kindly Suggest?
Thank You.
You cannot use bindings on the DynamicResource properties, as it does not derive from DependencyObject. You would either need to set the Style property directly from code-behind, or just use a Binding.
You could use a Style for the Button type, which has a DataTrigger based on a custom property that dynamically changes the look. But in this case, you need a single Style, which changes it's setters based on your condition. It would not allow you to change the Style property itself dynamically.
You can try this... I came up with a way to create a DynamicResourceBinding on which you can use a converter to achieve the results you want. (You theoretically could also just use styles and triggers, but I digress...)
How do you create a DynamicResourceBinding that supports Converters, StringFormat?

Categories

Resources