Making a Generic UserControl which can be accessed by other view - c#

I have UserControl A given below with two Radio Buttons.
This UserControl view has its ViewModel.
Question:
I again have two Views Create and Edit.
I want to use the above mentioned UserControl within Create/Edit with requirement that i can make the radiobuttons or any of the elements in UserControl to be Visible or Hidden based on the requirement in Create/Edit View.
Eg: Create May not require Radio button 1 and 2.So only Rectangle must be displayed.
Whatever input i give in the list or textbox must be updated in UserControl's ViewModel and the search result after clicking on button must be sent to Create/Edit accordingly.
Note:Create/Edit have their own ViewModels.Please suggest which approach is best considering MVVM
The Control has to be placed in the grayed out area as shown in rectangle for Create/Edit View

You can create DependancyProperty inside your UserControl like
public static readonly DependencyProperty RadioButtonVisibilityProperty=
DependencyProperty.Register( "RadioButtonVisibility", typeof(Visibility),
typeof(MyUserControl));
public Visibility RadioButtonVisibility
{
get { return (Visibility)GetValue(RadioButtonVisibilityProperty); }
set { SetValue(RadioButtonVisibilityProperty, value); }
}
and inside your UserControl's xaml Set the radiobutton's visibility like
<RadioButton Visibility="{Binding Parent.RadioButtonVisibility,ElementName=LayoutRoot}"/>
and in your main View(Create/Edit) do like this
<MyUserControl x:Name="Edit" RadioButtonVisibility="Visible"/>
or
<MyUserControl x:Name="Create" RadioButtonVisibility="Hidden"/>
And dont forget to give your UserControl's parent Grid the name "LayoutRoot"
like
<Grid x:Name="LayoutRoot"/>

It might be a good idea to have the UserControl be driven by some abstract BaseViewModel. Then you create two sub-classes EditViewModel and CreateViewModel which you then use based on the context.
Quick crude example for the radio buttons:
public abstract class BaseViewModel
{
public bool ShowRadioButtons { get; protected set; }
}
public class EditViewModel : BaseViewModel
{
public EditViewModel()
{
ShowRadioButtons = true;
}
}
public class CreateViewModel : BaseViewModel
{
public CreateViewModel()
{
ShowRadioButtons = false;
}
}

Related

How to achieve dynamic binding in WPF/MVVC C#

I am rather new to MVVC/wpf, having mostly worked with winforms.
What I want to accomplish is dynamic databinding without using code behind in WPF. The user interface consists of a devexpress grid and a couple of buttons. Each button press loads an object list and presents the objects in the grid. The lists contain different object types depending on the button pressed. For this example I have two classes to present: FatCat and FatDog.
In winforms this works:
private void button1_Click(object sender, EventArgs e)
{
((GridView)gridCtrl.MainView).Columns.Clear();
gridCtrl.DataSource = new BindingSource(itsModel.GetAll<FatDog>(), null);
}
private void button2_Click(object sender, EventArgs e)
{
((GridView)gridCtrl.MainView).Columns.Clear();
gridCtrl.DataSource = new BindingSource(itsModel.GetAll<FatCat>(), null);
}
I have configured the grid to create columns dynamically, so everything just works. itsModel is of type CatClientModel.
In wpf I have defined the DataContext to be CatClientModel.
What should I use for ItemsSource in the grid to achieve the same behaviour as my winforms solution?
dxg:GridControl ItemsSource="{Binding SomeDynamicList}"
In other words, what should SomeDynamicList be in the code above? Or am I going about this the wrong way?
I am, as I stated, using the DevExpress wpf grid control, but the question ought to be general and apply to any control presenting object lists.
In other words, what should SomeDynamicList be in the code above?
SomeDynamicList should be an ObservableCollection<T> property to which you can add any objects of type T that you want to display in the GridControl.
Set the DataContext of the GridControl, or any of its parent elements, to an instance of a class where this property is defined:
public class CatClientModel
{
public ObservableCollection<Animal> SomeDynamicList { get; } = new ObservableCollection<Animal>();
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new CatClientModel();
}
}
Ok. But the thing is that the ObservableCollection contains different types. Unfortunately there is no feasible class to inherit from. I want to bind to either ObservableCollection or ObservableCollection depending on which button was pressed
Switch the DataContext then, or change the property into an IEnumerable and set it to a new collection each time the button is clicked. This requires you to implement the INotifyPropertyChanged interface in your view model
private System.Collections.IEnumerable _collection;
public System.Collections.IEnumerable MyProperty
{
get { return _collection; }
set { _collection = value; OnPropertyChanged(); }
}
If you want to use XAML to define which data sources your code maps to for each grid that is possible. That does require at least some method of MVVM manager either prism or mvvmlight to connect the view model to the view.
so if you do go the MVVM model route, the Model would contain a description for each of your grids like this:
public BulkObservableCollection<icd10facet> FacetList
{
get { return this._facets; }
set { SetProperty(ref this._facets, value); }
}
public INotifyTaskCompletion<BulkObservableCollection<PetsConvert>> ConceptList
{
get { return this._concept; }
set
{
SetProperty(ref this._concept, value);
}
}
In the XAML for your code the grid woud bind to the grid defined by ConceptList in this way:
ItemsSource="{Binding ConceptList.Result}"
this answer does NOT address how to wire up Prism 6.0 for example to use a view model but for examples see:
https://github.com/PrismLibrary/Prism
Which contains documentation and starter code. Keep in mind that there is not any specific reason that putting code in the code behind for the view is a problem, first solve the problem and then refactor if separation of concerns is an issue for you.
Using this technique you can bind each grid to its own data source. In the MVVM space buttons and other things use a commanding model to communicate with the view model.
<Button Content="Load Rule Data" Width="100" Height="40" HorizontalAlignment="Left" Margin="5px" Command="{Binding LoadRuleData }"/>
this requires defining a command delegate in the viewmodel for LoadRuleData
public DelegateCommand LoadRuleData { get; private set; }
and then (usually in the constructor) wire the DelegateCommand to the method that is going to do the work.
this.LoadRuleData = new DelegateCommand(this.loadRules);

UWP (XAML / C#) access method from parent Page within a programmatically added UserControl

I've created a UWP XAML Page where I programmatically add a UserControl and then is displayed in a ContentControl.
UserControl control = new Views.PlayingGameScreen();
this.ContentControl.Content = control;
I'm wondering how I access a method in the parent Page from the UserControl.
I think the cleanest way is to expose a property on the UserControl that the parent sets.
public partial class PlayingGameScreen : UserControl
{
public ICommand Command { get; set; }
public void SomeMethod()
{
this.Command?.Execute(null);
}
}
//parent code, define the command with the logic to run
var command = new DelegateCommand(...);
var control = new Views.PlayingGameScreen
{
Command = command
};
this.ContentControl.Content = control;
By doing it this way, the parent sets the Command and the UserControl doesn't need to know what the hosting parent is and there is no global class the UserContol uses. This is a completely decoupled solution.
Bonus, setup the Command as a DependencyProperty and then it can be assigned in xaml as well. You can bind the Command to your ViewModel and the ViewModel will be the Page's ViewModel.
<uc:PlayingGameScreen Command="{x:Bind SomeCommand}"/>
I've tried lots of different ways and this is my favorite.
From my little experience with UWP (1 app and counting :D):
You can keep a reference of your ParentPage in a static property of a class, let's name it PagesGateway.cs
The static class code:
public static class PagesGateway
{
public static ParentPage ParentPage {get; set;}
}
Then in you code behind ParentPage, you'll need to set the reference:
public partial ParentPage:Page
{
public ParentPage{
PagesGateway.ParentPage = this;
}
}
This way you'll have access to ParentPage instance methods anywhere.
But I have a feeling that's not the best practice out there. But i use this a lot to access my ViewModels from anywhere, and it work perfectly, but you have to take into account that it void the re-usability of a ViewModel

How to expose combobox Databindings on a usercontrol

I have a combobox on a usercontrol. I can expose the datasource however I cant expose the actual bindings.
If you add a normal combobox to a form and go to the databindings property you can choose selected value, text etc.
After this is chosen the designer automatically creates a
combobox.databindings.add("SelectedValue", datasource, columname, true));
How can I expose a combobox on a user control so that it has the above behavior
It's probably not considered best practice to expose your controls like this since after all, part of the point of using a UserControl is to hide the details of the child controls.
Try exposing the control on the UserControl as a property:
public partial class UserControl1 : UserControl {
public UserControl1() {
InitializeComponent();
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ComboBox ComboBox {
get {
return this.comboBox1;
}
}
}
If you are only interested in the control's DataBindings, then try to just expose that information:
public partial class UserControl1 : UserControl {
public UserControl1() {
InitializeComponent();
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ControlBindingsCollection ComboDataBindings {
get {
return this.comboBox1.DataBindings;
}
}
}

WPF Custom Controls

I've been trying for ever to try and figure this out.
Story: I have one MainWindow and 2 User Controls.
When the MainWindow loads One control is visible and the other is not.
Once the user enters their data and settings, I need to make the other form visible.
The form that is invisible at startup needs to be initialized, because it is gathering data from the WMI of the computer it is running on. It is also gathering AD Information in preparation for the user.
For some reason I cannot get one form to show the other.
I think this is what I'm supposed to be looking at:
#region Class Variable
public string ShowSideBar { get { return (String)GetValue(VisibilityProperty); } set { SetValue(VisibilityProperty, value); }}
public DependencyProperty VisibilityProperty = DependencyProperty.Register("ShowSideBar", typeof(string), typeof(UserControl), null);
#endregion
This is set in my MainWindow Class, however, I have no idea why I cannot call it from any other usercontrol.
Is there any way to just expose something like this to all my forms from my MainWindow?
public int RowSpan {
get { return Grid.GetRowSpan(DockPanel1); }
set { Grid.SetRowSpan(DockPanel1,value); }
}
Dependency properties must be static. Why is the type string? Should it not be Visibility if you wish to bind the visibility of the controls to it?
Does it have to be a dependency property? You could just use a regular property as well and implement INotifyPropertyChanged, since you are not binding this field to anything, rather binding other things to it.
For a dependency property, try something like this instead:
public static readonly DependencyProperty SideBarVisibilityProperty = DependencyProperty.Register("SideBarVisibility", typeof(Visibility), typeof(MyTemplatedControl), null);
public Visibility SideBarVisibility
{
get { return (Visibility)GetValue(SideBarVisibilityProperty); }
set { SetValue(SideBarVisibilityProperty, value); }
}
Firstly, this application would benefit from application of the MVVM pattern.
However, without taking that approach, you can still resolve the problem you have. It would be unusual for a user control to rely on knowing what its parent is. The code behind for your main window would be the better place to put this code. (Not as good as a view model... but that's another story.)
Add to the control that should cause the side bar to be made visible an event, ShowSideBar. Attach a handler in the main window, and use the handler to display the second control. No need for dependency properties here at all.
public class MyControl : UserControl
{
...
public event EventHandler ShowSideBar;
// Call this method when you need to show the side bar.
public void OnShowSideBar()
{
var s = this.ShowSideBar;
if (s != null)
{
s(this, EventArgs.Empty);
}
}
}
public class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
this.FirstControl.ShowSideBar += (s, e) =>
{
this.SecondControl.Visibility = Visibility.Visible;
}
}
}
I fixed the initlized Component but changing.
X:Class="AdminTools.MainWindow.ShowSideBar" to x:Class="AdminTools.MainWindow".
now i have an issues where
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:User="clr-namespace:AdminTools.Controls.User"
xmlns:Custom="clr-namespace:AdminTools.Controls.Custom"
xmlns:Bindings="clr-namespace:AdminTools.Functions"
x:Class="AdminTools.MainWindow"
Title="MainWindow" Height="691.899" Width="1500"
>
<Window.DataContext>
<Bindings:ShowSideBar />
</Window.DataContext>
<Bindings:ShowSideBar /> = ShowSideBar does not exist in the namespace clr-namespace:AdminTools.Functions
ShowSideBar: member names cannot be the same as their enclosing type.

WPF - binding two properties on the different dialogs

I have two options on different dialogs (classes) related to CheckBox in different XAML files:
First pair:
C#:
public class FirstClass : DependencyObject
{
public static readonly DependencyProperty testProperty =
DependencyProperty.Register("testProperty", typeof(bool),
typeof(FirstClass),
new UIPropertyMetadata(false));
public bool testProperty
{
get { return (bool)this.GetValue(testProperty); }
set { this.SetValue(testProperty, value); }
}
}
XAML:
<CheckBox IsChecked="{Binding Path=testProperty, Mode=TwoWay}">
Second pair:
C#
public class SecondClass : DependencyObject
{
public static readonly DependencyProperty testProperty =
FirstClass.testProperty.AddOwner(typeof(SecondClass));
public bool testProperty
{
get { return (bool)this.GetValue(testProperty); }
set { this.SetValue(testProperty, value); }
}
}
XAML:
<CheckBox IsChecked="{Binding Path=testProperty, Mode=TwoWay}">
I want to bind the option from the first dialog to the option from the second dialog (A<=>B). If the CheckBox in the first dialog is checked, the CheckBox in the second dialog should also be checked. Should I use ApplicationSettings for this purpose?
DataBinding is not only the process of binding between two dependency properties, but also between one CLR and one dependency property. In fact it usually is the most common binding scenario. So what you can do is use a model object which stores the value of your ui elements and reuse it in another view or another control.
First of all i advise you not to use a DependencyObject derived object as your data holder, while its still valid it has it cons aswell.
First our data object, which stores just our data. Please lookup how to implement the INotifyPropertyChanged interface, because i left the implementation for readability.
class DataHolder : INotifyPropertyChanged
{
public bool MyValue
{
get{return mMyValue;}
set{mMyValue = value; RaiseProperty("MyValue");}
}
private bool mMyValue;
}
This object can now be easily bound to your ui elements with the help of the UI DataContext. Its vital to know that the DataContext is an inherited dependency property, which means that in the tree of controls, if an element doesn't have a set datacontext it automatically gets the datacontext of his parent. Think of a login dialog with user name and password. If you have a simple model with two properties like user name and password, you just set this model as the datacontext to the dialog and all controls can bind to these properties. Back to your example, you just set an instance of DataHolder to your window datacontext property
public MainWindow()
{
InitializeComponents();
var model = new DataHolder();
DataContext = model;
}
and now you can use bindings in your xaml like this
<CheckBox IsChecked="{Binding MyValue, Mode=TwoWay}"/>
Multiple controls can bind to the same property, and you can use the same model when you open another view.
One last advice, you should read the DataBinding chapter of the msdn and its a good idea to look into the MVVM pattern because its widely used with WPF to great success. We use it for a rather large application and are very satisfied with it.

Categories