FirstView.xaml contains Something like this
<ContentControl Name="ContentControlName" Grid.Row="1" Grid.Column="0" Content="{Binding SelectionViewModel}"/>
My SelectionView.xaml contains
<TextBox x:Name="textBoxName" Text="{Binding Name}"/>
<TextBox IsReadOnly="True" Text="{Binding Uid}"/>
In the FirstViewModel I have created a property like below
private SelectionViewModel selectionViewModel;
public SelectionViewModel SelectionViewModel
{
get
{
return this.selectionViewModel;
}
}
Content control with two text box is not displayed when I run
Is the way done right?
Since you used binding, you need raise up PropertyChanged event.
Your ViewModel class (SelectionViewModel) must implement INotifyPropertyChanged.
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private SelectionViewModel selectionViewModel;
public SelectionViewModel SelectionViewModel
{
get
{
return this.selectionViewModel;
}
private set
{
this.selectionViewModel = value;
PropertyChanged?.Invoke(this, nameof(SelectionViewModel));
}
}
}
You passed SelectionViewModel instance to Content property of ContentControl.
Your ContentControl must have special datatemplate coupled with this view model. Otherwise, it will not work.
For example:
<ContentControl Content="{Binding SelectionViewModel}">
<ContentControl.ContentTemplate>
<DataTemplate DataType="{x:Type local:SelectionViewModel}">
<!-- Here is your template -->
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
Also you shouldn't use same names for type SelectionViewModel and property SelectionViewModel.
Since you wasn't provide a source code we can't figure out the exact cause of your error.
I hope it was helpful for you.
Related
I have a Grid and want to show user control as child of the grid or content of the grid. When a button is clicked, a few user controls will be shown dependent on the cases. Please check the xaml part and code behind.
<Grid x:Name="ContentPanel" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="5,5,5,5">
</Grid>
I want to bind Grid content to the below activeUserControl object.
public class MainVM
{
public UserControl activeUserControl;
Stability stability;
Tank tank;
public MainVM()
{
stability = new Stability();
tank = new Tank();
activeUserControl = stability;
stability.Visibility = Visibility.Visible;
}
}
The problem is that you cannot directly bind to the Children collection of a Grid, because it is not a DependencyProperty. You would have to implement attached properties or a behavior to do so. However, you can put a ContentControl into your Grid or replace it as a workaround. Then bind its Content to the activeUserControl property in your view model. Usually properties start with a capital letter, so I adapted it.
<Grid x:Name="ContentPanel" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5,5,5,5">
<ContentControl Content="{Binding ActiveUserControl}"/>
</Grid>
Make sure that your MainVM is set as DataContext in any of the parent controls, otherwise this binding will not work. activeUserControl must be a property to make it bindable. Implement INotifyPropertyChanged in your MainVM, so that the ContentControl gets notified when the property changes and adapts its Content.
// Implement "INotifyPropertyChanged" so controls get notified about property changes
public class MainVM : INotifyPropertyChanged
{
// Backing field of the "ActiveUserControl" property
private UserControl _activeUserControl;
public UserControl ActiveUserControl
{
get => _activeUserControl;
set
{
// Only set the value if it has changed
if (_activeUserControl != value)
{
_activeUserControl = value;
// Signal to the control that it needs to update the value
OnPropertyChanged(nameof(ActiveUserControl));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public MainVM()
{
// ...your code.
ActiveUserControl = stability;
// ...your code.
}
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
This should solve your problem, but your code is still a mix of MVVM and code-behind. Ideally, you should not have any UI control references in a view model.
Consider creating Tank and Stability view models using INotifyPropertyChanged and data templates to display them, instead of UserControls in your view model. You can still use the ContentControl in this case.
<ContentControl Content="{Binding activeUserControl}">
<ContentControl.Resources>
<DataTemplate DataType={x:Type TankViewModel}>
<!-- ...data template equivalent of your "Tank" user control. -->
</DataTemplate>
<DataTemplate DataType={x:Type StabilityViewModel}>
<!-- ...data template equivalent of your "Stability" user control. -->
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
So I just setup a project and added a custom UserControl that looks like this.
<Grid>
<ItemsControl ItemsSource="{Binding UserViewModel.Users}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<controls:UserCard/>
<TextBlock Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
As you can see I tried binding the Text property buti it doesn't bind.
Now there could be a lot of reasons to why it's behaving like this so I will try to narrow it down.
I've created a BaseViewModel that will hold my ViewModels and it looks like this.
public class BaseViewModel : ObservableObject
{
public UserViewModel UserViewModel { get; set; } = new UserViewModel();
}
And then I've setup my ViewModel like this
public class UserViewModel : ObservableObject
{
public ObservableCollection<User> Users { get; set; } = new ObservableCollection<User>();
public UserViewModel()
{
Users.Add(new User{Name = "Riley"});
Users.Add(new User{Name = "Riley1"});
}
}
Simple, now I do have a ObservableObject that looks like this and deals with the INPC
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And in my MainView.xaml
I've set the DataContext like so
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new BaseViewModel();
}
}
It's the exact same for the UserControl
And this is where I actually add the UserControl so it displays in the MainWindow
<ItemsControl ItemsSource="{Binding UserViewModel.Users}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<controls:UserCard/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now the issue is that it doesn't bind the Data, I want to display the Name property from the Model but it's not displaying it and I am not sure why, if I try to bind it to a TextBlock property in the MainView directly it works fine.
I am unsure to why it's behaving like this and I would like to understand why.
Do I need to make use of DependencyProperties? Or is it just a case of me creating a new instance of the BaseViewModel? Where did I go wrong?
Your MainViewWindow contains an ItemsControl with the binding ItemsSource="{Binding UserViewModel.Users}", with each item being displayed with a <controls:UserCard/>. But your user control is then trying to bind to the list again with "{Binding UserViewModel.Users}". Why are you trying to display a list inside another list?
I suspect the problem here is that you think your custom UserControl's DataContext is still pointing to the BaseViewModel, like its parent. It isn't. The DataContext of each item in an ItemsControl points to it's own associated element in the list, i.e. an instance of type User.
UPDATED: Let's say you have a main view model with a list of child view models, like this:
public class MainViewModel : ViewModelBase
{
public MyChildViewModel[] MyItems { get; } =
{
new MyChildViewModel{MyCustomText = "Tom" },
new MyChildViewModel{MyCustomText = "Dick" },
new MyChildViewModel{MyCustomText = "Harry" }
};
}
public class MyChildViewModel
{
public string MyCustomText { get; set; }
}
And let's say you set your MainWindow's DataContext to an instance of MainViewModel and add a ListView:
<ListView ItemsSource="{Binding MyItems}" />
If you do this you'll see the following:
What's happening here is that the ListView is creating a container (of type ContentPresenter) for each of the three elements in the list, and setting each one's DataContext to point to its own instance of MyChildViewModel. By default ContentPresenter just calls 'ToString()' on its DataContext, so you're just seeing the name of the class it's pointing to. If you add a ToString() operator to your MyChildViewModel like this:
public override string ToString()
{
return $"MyChildViewModel: {this.MyCustomText}";
}
... then you'll see that displayed instead:
You can also override the ListViewItem's template entirely, and since it already points to its associated instance of MyChildViewModel you can just bind directly to its properties:
<ListView ItemsSource="{Binding MyItems}">
<ListView.ItemTemplate>
<DataTemplate>
<!-- One of these gets created for each element in the list -->
<Border BorderBrush="Black" BorderThickness="1" Background="CornflowerBlue" CornerRadius="5" Padding="5">
<TextBlock Text="{Binding MyCustomText}" Foreground="Yellow" />
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Which will change the display to this:
Make sense?
I'm trying to figure out the correct way to bind a ViewModel to a ContentControl (I've looked all over the net but can't find an example that I can get to work correctly).
My Model:
public class Model
{
private string _Variable = "TEST";
public string Variable
{
get { return _Variable; }
set { _Variable = value; }
}
}
My ViewModel
public class ViewModel :ViewModelBase
{
private Model _Model = new Model();
public string Variable
{
get { return _Model.Variable; }
set
{
if (_Model.Variable != value)
{
_Model.Variable = value;
RaisePropertyChanged("Variable");
}
}
}
My View/Window
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type System:String}">
<TextBox/>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ContentControl Content="{Binding Path=Variable}" />
</StackPanel>
So in essence, I have (or at least I believe I have) set the content of the ContentControl to the ViewModel property 'Variable', it is of type string so the only DataTemplate should be implemented and a Textbox displayed.
And that happens... A Textbox is displayed! However the Textbox is empty, and any changes made do not impact Variable.
This means I have made an error in the Batabinding, but I don't understand where. I have a feeling that just because my DataTemplate is displaying a Textbox, nothing is actually binding the string to it, but that is the bit I'm lost over.
Thanks for any help/advice.
You haven't specified the TextBox's Text binding, which is completely separate to the DataContext. Since you want the TextBox to bind to its own DataContext just do this:
<TextBox Text="{Binding Path=.}"/>
use textbox as below:
<TextBox Text="{Binding}" />
I have 5 CheckBox and this is what they look like in the View:
<CheckBox IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" Content="Cb1"/>
<CheckBox IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" Content="Cb2"/>
<CheckBox IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" Content="Cb3"/>
<CheckBox IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" Content="Cb4"/>
<CheckBox IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" Content="Cb5"/>
This is the some of the code that I have in my ViewModel:
class CheckBoxesViewModel : INotifyPropertyChanged
{
public CheckBoxesViewModel()
{
CheckBoxes= new ObservableCollection<Models.CheckBoxes>();
_canExecute = true;
}
private bool _IsSelected;
public bool IsSelected
{
get
{
return _IsSelected;
}
set
{
_IsSelected = value;
OnPropertyChanged("IsSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private ObservableCollection<Models.CheckBoxes> _checkBoxes = new ObservableCollection<Models.CheckBoxes>();
public ObservableCollection<Models.CheckBoxes> CheckBoxes
{
get { return _checkBoxes ; }
set
{
_checkBoxes = value;
OnPropertyChanged("CheckBoxes");
}
}
}
The problem is that when I check/uncheck one of the checkboxes it affects all of them.
I assume that is because they have exact same binding, but I can not figure out how to make the code distinguish them.
I think I could use Command and CommandParameters, but that does not seem like the best solution.
P.S. Do let me know if you see something wrong with my code - I am still trying to learn the whole MVVM thing.
You need to implement ICommand (google DelegateCommand to be able to treat an Action as ICommand) then you would bind the Command property of the CheckBox in the view to the Command on the view model.
ViewModel
public ICommand MyCommand { get; private set; }
.... MyCommand = new DelegateCommand((value) => this.DoStuff(value));
Xaml
<CheckBox Command={Binding MyCommand} Command Parameter={...} />
From your stated purpose in comments (which really should have been in your question--this is a classic XY problem)
You're attempting to route View logic through your ViewModel, which should be a hint that something's wrong here. Your stated purpose is
Each CheckBox has a corresponding TextBox that gets shown when it is checked. I was hoping to reuse the same code for all of the CheckBoxes and only change some value that helps me distinguish them (e.g. Content)
Toggling visibility is a View concern. You can do it thusly
<StackPanel>
<CheckBox x:Name = "cb1" />
<!-- cb2 through cbn omitted -->
<StackPanel />
<StackPanel>
<StackPanel.Resources>
<BooleanToVisibilityConverter x:Key="btvc" />
</StackPanel.Resources>
<TextBox Text="{Binding FirstTextBox}"
Visibility="{Binding IsChecked,
ElementName=cb1,
Converter={StaticResource btvc}}" />
<!-- SecondTextBox through NthTextBox omitted -->
</StackPanel />
I'm toggling visibility of the textbox by whether or not the corresponding checkbox was checked.
Now, if you're trying to munge together N textbox values into one property... You're making life too hard on yourself.
If you wish to 'programmatically add them", then you need to reverse your logic. Instead of 'adding controls' to the form, you need to think of 'adding data' to your ViewModel. This is how you stay within MVVM guidelines, as your tag suggests.
Here's how you can reverse the logic....The ItemsControl has the ability to bind to a collection of ViewModels. This control also 'automagically' determines what control to use for each item in the collection by using DataTemplates.
Here's the XAML code:
<ItemsControl ItemsSource="{Binding myCollection}" >
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:myViewModelForItemA}">
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding aName}"></CheckBox>
</DataTemplate>
<DataTemplate DataType="{x:Type local:myViewModelForItemB}">
<RadioButton IsChecked="{Binding IsChecked}" Content="{Binding aName}"></RadioButton>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
As you can probably see, the Binding "myCollection" is the collection you add your ViewModel Instances to (this is an ObservableCollection).
Each DataTemplate in the IntemsControl.Resources is how you want each item to look (you can even place multiple controls inside each DataTemplate, just remember that whatever you bind these to will bind to the ViewModel in the collection (i.e. myViewModelForItemA, myViewModelForItemB).
The code in your main view model:
public ObservableCollection<object> myCollection { set; get; }
....
myCollection = new ObservableCollection<object>();
myViewModelForItemA anItem = new myViewModelForItemA();
myCollection.Add(anItem);
//now anItem of type (myViewModelForItemA) is in our collection
//and the ItemsControl automagically added a CheckBox to it's collection
//and bound isChecked to anItem.isChecked property, and bound
//the Content to anItem.aName property.
While it is trivial to store a checkbox's checked state in a variable using the checkbox's Click event, how would I do it via databinding? All the examples I have found have the UI updated from some datasource, or bind one control to another; I want to update a member variable when the checkbox is clicked.
TIA for any pointers...
You must make your binding bidirectional :
<checkbox IsChecked="{Binding Path=MyProperty, Mode=TwoWay}"/>
You need a dependency property for this:
public BindingList<User> Users
{
get { return (BindingList<User>)GetValue(UsersProperty); }
set { SetValue(UsersProperty, value); }
}
public static readonly DependencyProperty UsersProperty =
DependencyProperty.Register("Users", typeof(BindingList<User>),
typeof(OptionsDialog));
Once that is done, you bind the checkbox to the dependency property:
<CheckBox x:Name="myCheckBox"
IsChecked="{Binding ElementName=window1, Path=CheckBoxIsChecked}" />
For that to work you have to name your Window or UserControl in its openning tag, and use that name in the ElementName parameter.
With this code, whenever you change the property on the code side, you will change the textbox. Also, whenever you check/uncheck the textbox, the Dependency Property will change too.
EDIT:
An easy way to create a dependency property is typing the snippet propdp, which will give you the general code for Dependency Properties.
All the code:
XAML:
<Window x:Class="StackOverflowTests.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" x:Name="window1" Height="300" Width="300">
<Grid>
<StackPanel Orientation="Vertical">
<CheckBox Margin="10"
x:Name="myCheckBox"
IsChecked="{Binding ElementName=window1, Path=IsCheckBoxChecked}">
Bound CheckBox
</CheckBox>
<Label Content="{Binding ElementName=window1, Path=IsCheckBoxChecked}"
ContentStringFormat="Is checkbox checked? {0}" />
</StackPanel>
</Grid>
</Window>
C#:
using System.Windows;
namespace StackOverflowTests
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public bool IsCheckBoxChecked
{
get { return (bool)GetValue(IsCheckBoxCheckedProperty); }
set { SetValue(IsCheckBoxCheckedProperty, value); }
}
// Using a DependencyProperty as the backing store for
//IsCheckBoxChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckBoxCheckedProperty =
DependencyProperty.Register("IsCheckBoxChecked", typeof(bool),
typeof(Window1), new UIPropertyMetadata(false));
public Window1()
{
InitializeComponent();
}
}
}
Notice how the only code behind is the Dependency Property. Both the label and the checkbox are bound to it. If the checkbox changes, the label changes too.
Hello this is my first time posting so please be patient:
my answer was to create a simple property:
public bool Checked { get; set; }
Then to set the data context of the Checkbox (called cb1):
cb1.DataContext = this;
Then to bind the IsChecked proerty of it in the xaml
IsChecked="{Binding Checked}"
The code is like this:
XAML
<CheckBox x:Name="cb1"
HorizontalAlignment="Left"
Margin="439,81,0,0"
VerticalAlignment="Top"
Height="35" Width="96"
IsChecked="{Binding Checked}"/>
Code behind
public partial class MainWindow : Window
{
public bool Checked { get; set; }
public MainWindow()
{
InitializeComponent();
cb1.DataContext = this;
}
private void myyButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Checked.ToString());
}
}
Should be easier than that. Just use:
<Checkbox IsChecked="{Binding Path=myVar, UpdateSourceTrigger=PropertyChanged}" />
if you have the property "MyProperty" on your data-class, then you bind the IsChecked like this.... (the converter is optional, but sometimes you need that)
<Window.Resources>
<local:MyBoolConverter x:Key="MyBoolConverterKey"/>
</Window.Resources>
<checkbox IsChecked="{Binding Path=MyProperty, Converter={StaticResource MyBoolConverterKey}}"/>
This works for me (essential code only included, fill more for your needs):
In XAML a user control is defined:
<UserControl x:Class="Mockup.TestTab" ......>
<!-- a checkbox somewhere within the control -->
<!-- IsChecked is bound to Property C1 of the DataContext -->
<CheckBox Content="CheckBox 1" IsChecked="{Binding C1, Mode=TwoWay}" />
</UserControl>
In code behind for UserControl
public partial class TestTab : UserControl
{
public TestTab()
{
InitializeComponent(); // the standard bit
// then we set the DataContex of TestTab Control to a MyViewModel object
// this MyViewModel object becomes the DataContext for all controls
// within TestTab ... including our CheckBox
DataContext = new MyViewModel(....);
}
}
Somewhere in solution class MyViewModel is defined
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool m_c1 = true;
public bool C1 {
get { return m_c1; }
set {
if (m_c1 != value) {
m_c1 = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("C1"));
}
}
}
}
No backend and ViewModel Code:
I made such check box to control other control's visibility.
<CheckBox x:Name="rulerCheckbox" Content="Is Ruler Visible" IsChecked="True"/>
and in the other control, I added such binding:
Visibility="{Binding IsChecked, ElementName=rulerCheckbox, Mode=TwoWay, Converter={StaticResource BoolVisConverter}}">