Bindings in nested WPF DataTemplates - c#

I have some WPF control called Foo. Grid structure with DevExpress LoadingDecorator looks like that:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="60" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<dx:LoadingDecorator Name="Decorator" IsSplashScreenShown="{Binding Path=ShowLoader}" SplashScreenLocation="CenterWindow">
<dx:LoadingDecorator.SplashScreenTemplate>
<DataTemplate>
<dx:WaitIndicator DeferedVisibility="True">
<dx:WaitIndicator.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="Operation:" FontSize="15"/>
<TextBlock Text="{Binding Path=CurrLoadStat}"/>
</StackPanel>
</DataTemplate>
</dx:WaitIndicator.ContentTemplate>
</dx:WaitIndicator>
</DataTemplate>
</dx:LoadingDecorator.SplashScreenTemplate>
</dx:LoadingDecorator>
</StackPanel>
...
</Grid>
Base ViewModel class implements INotifyPropertyChanged interface and ViewModel class (FooViewModel) used as DataContext for control with Grid described above inherits from it. I have implemented property to change text property in 2nd TextBlock element:
private string currLoadStat = "Preparing data...";
public string CurrtLoadStat
{
get
{
return currLoadStat;
}
set
{
currLoadStat = value;
OnPropertyChanged("CurrLoadStat");
}
}
My problem is that binding instruction doesn't work and I see only text defined in first TextBlock.
Can You provide me some solution to resolve this problem?

Your property has a "t" in the middle of the name but your binding path (XAML) and magic string you pass to OnPropertyChanged do not. The string you pass when you raise the PropertyChanged event method must exactly match your view model's property name.
If you are using C# 5 or 6 then switch to using one of the approaches outlined here and you would eliminate the need for passing magic strings.

Related

Navigation.PushAsync is not working inside User Control in Xamarin forms

I have a ListView in my ContentPage. Inside ListView.ItemTemplate, I have user control. In user control, there are image buttons. On Click of ImageButton, I want to move another page but it's not working. The code is run without error but nothing happened. The same code of PushAsync is working from all other content pages but not in user control. Please help me in this regard to what should I do.
ListView Code inside ContentPage:
<ListView x:Name="lvPosts" HasUnevenRows="True" SeparatorVisibility="None"
IsVisible="False">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10">
<controls:CardView/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
UserControl CardView:
<Frame xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local="clr-namespace:MyApp.General"
VerticalOptions="Start"
x:Class="MyApp.Controls.CardView">
<Frame.Content>
<Grid Padding="10" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Grid.Column="0"
Grid.ColumnSpan="4"
Grid.Row="0"
Text="{Binding Title}"
HorizontalOptions="FillAndExpand"
TextColor="#479774"
FontSize="Medium"
Style="{StaticResource DefaultFontStyle}"
Opacity="0.8">
</Label>
<StackLayout Orientation="Vertical" Grid.Row="1" Grid.Column="0">
<ImageButton
x:Name="imgPdf"
Source="pdf.png"
Aspect="AspectFill"
Clicked="imgPdf_Clicked"/>
</StackLayout>
<StackLayout Orientation="Vertical" Grid.Row="1" Grid.Column="1">
<ImageButton
x:Name="imgContent"
Source="document2.png"
Aspect="AspectFill"
Clicked="imgContent_Clicked"/>
</StackLayout>
<StackLayout Orientation="Vertical" Grid.Row="1" Grid.Column="2">
<ImageButton
x:Name="imgSummary"
Source="document.png"
Aspect="AspectFill"
Clicked="imgSummary_Clicked"/>
</StackLayout>
<StackLayout Orientation="Vertical" Grid.Row="1" Grid.Column="3">
<ImageButton
x:Name="imgVideo"
Source="video1.png"
Aspect="AspectFill"
Clicked="imgVideo_Clicked"/>
</StackLayout>
</Grid>
</Frame.Content>
</Frame>
And Code behind on ImageButton:
private async void imgVideo_Clicked(object sender, EventArgs e)
{
Post post = this.BindingContext as Post;
await Navigation.PushAsync(new WebViewPage(post));
}
The above answer isn't quite correct, and makes some assumptions. Navigation is available and can be used by any Xamarin Forms component that inherits NavigableElement and has a NavigationPage as an ancestor in its parental hierarchy. It's not limited to ContentPage by any means. Internal to Xamarin Forms, NavigableElements access their ancestral NavigationPage's Navigation stack via a series of internal NavigationProxy classes that get set and bound to their parent's NavigationProxy when each element's parent is set. Externally, we see these proxies as their interface type of INavigation, typically as the Navigation property of whatever element we're using.
ViewCell does not inherit NavigableElement (why, I'm not entirely sure, since it's parented to its container like any other component), and thus doesn't have a Navigation property. In your case, your ViewCell contains another View, which does inherit NavigableElement, but because its parent (ViewCell) isn't a NavigableElement, the chain of NavigationProxies is broken. Thus, you can try to make calls to Navigation on your CardView, but it's not hooked up to anything, and thus won't do anything.
The solution of directly accessing the Application's Navigation property (I assume the above poster meant Application.Current.MainPage.Navigation, as Application doesn't have a Navigation property itself) only works if your Application's MainPage is the only NavigationPage in your app. However, this isn't guaranteed to be the case; for example, a TabbedPage component could contain its own NavigationPages for each of its tabs, and accessing Application.Current.MainPage.Navigation will only get you the NavigationProxy for navigating the MainPage. ViewCells in a tabbed NavigationPage would be unable to navigate within their tab via this solution.
What you can do instead is something like this:
public MyViewCell : ViewCell
{
private INavigation Navigation { get; set; }
protected override void OnParentSet()
{
base.OnParentSet();
Navigation = (Parent as NavigableElement)?.Navigation;
}
}
Your ViewCell now has access to its parent's navigation stack, which you can do with as you please. You could pass it into your cell's child View, if you wanted, or just do your navigation here. Why ViewCell doesn't already do this under the hood, I don't fully understand.
Note: Depending on the CacheStrategy you set on your ListView (or whatever View component is using your ViewCell as a template), it's possible your custom ViewCell may be handed a null Parent from time to time, which would yield a null Navigation property. Usually this shouldn't be a problem, as a null Parent generally indicates the cell isn't actively being displayed, but it's worth keeping in mind in case your custom ViewCell could potentially try to access Navigation without user input via some automated business logic.
We could only invoke the method Navigation.PushAsync in a ContentPage . In your case , CardView is a subclass of a Frame , so it will never work .
The best solution is to invoke the method in Code Behind (ViewModel or ContentPage) . Since you had used Custom View . It would be better to handle the logic by using Data-binding .
If you do want to invoke the method in CardView, you could define a property in App and pass the current Navigation from ContentPage .
in App.xaml.cs
public INavigation navigation { get; set; }
in ContentPage
Note : You need to invoke the following lines in each ContentPage .
public xxxContentPage()
{
InitializeComponent();
var app = App.Current as App;
app.navigation = this.Navigation;
}
in CardView
var app = App.Current as App;
var navigation = app.navigation;
navigation.PushAsync(xxx);

Binding a Text property for filtering within a popup that is part of a DataTemplate not working

I have an application that displays a datagrid. However the data has gotten big and I want to incorporate filters to some of the rows. I've gotten as far as creating a DataTemplate for my headers:
<DataGrid>
<DataGrid.Resources>
...
<DataTemplate x:Key="HeaderTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ContentControl Content="{Binding}" VerticalAlignment="Center"/>
<ToggleButton Name="FilterButton" Grid.Column="1" Content="▼" Margin="2, 1, 1, 1" Padding="1, 0"/>
<Popup IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}" PlacementTarget="{Binding ElementName=FilterButton}" StaysOpen="False">
<Border Background="White" Padding="3">
<TextBox Text={Binding PetNameFilterSearchBox, Mode=TwoWay} Width="300"/> <!--The Text Box I want to bind-->
</Border>
</Popup>
</Grid>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Width="6*" Header="Pet Name" Binding="{Binding PetName}" ElementStyle="{DynamicResource DataGridTextColumnWrap}" HeaderTemplate="{StaticResource HeaderTemplate}"/>
</DataGrid.Columns>
</DataGrid>
So far what it does is show a button next to the header text and when you click on it a small popup window appears containing a text box. The desired effect is that the user can type in the text box and data will be filtered according to what was typed.
In my view model I already have my filter text box property that I want to use for binding:
public string PetNameFilterSearchBox
{
get
{
return _petNameFilterSearchBox;
}
set
{
_petNameFilterSearchBox = value;
RaisePropertyChanged(nameof(PetNameFilterSearchBox));
FilterData(); //As you're writing
}
}
private string _petNameFilterSearchBox = string.Empty;
public CollectionView PetDataFilterView { get; set; }
public bool OnFilterTriggered(object item)
{
if (item is AvailablePetInfo petInfo)
{
var pet_name = PetNameFilterSearchBox;
if (pet_name != string.Empty)
return (petInfo.DisplayName.Contains(pet_name));
}
return true;
}
public void FilterData()
{
CollectionViewSource.GetDefaultView(AvailablePetInfo).Refresh();
}
//Constructor
public PetInfoViewModel()
{
AvailablePetInfo = GetPetInfo();//gets the list
ContactFilterView = (CollectionView)CollectionViewSource.GetDefaultView(AvailablePetInfo);
ContactFilterView.Filter = OnFilterTriggered;
}
When I run my code I see the little button next to the header, I click on it and I see the textbox. But when I start typing I dont see my datagrid updating. I set some breakpoints in my PetNameFilterSearchBox and I find that when I start typing it's not getting hit. This tells me that there's something wrong with the binding. Can someone tell me what I'm doing wrong?
Your problem is one of DataContext.
I'll be assuming PetNameFilterSearchBox is a property of the Window hosting the DataGrid and that the appropriate DataContext is set at the Window level.
Normally, DataContext is inherited by child elements, so setting the DataContext for the Window would set it for all its children. But things change once you start using DataTemplates.
In a DataTemplate, the root DataContext is always the data object that's being displayed. In your case, that's the string "Pet Name". This is why you can put <ContentControl Content="{Binding}"/> inside the DataTemplate and have it display "Pet Name".
The downside is you can't put <TextBox Text="{Binding PetNameFilterSearchBox}"/> and expect it to bind to the Window, because that DataContext is being overridden by the DataTemplate.
Normally, you can get around the DataTemplate DataContext problem by using RelativeSource, which you can use walk up the visual tree and find another source to bind to. But this doesn't work inside a Popup because a Popup is not actually part of the Window's visual tree.
What will work is ElementName:
<TextBox Text="{Binding PetNameFilterSearchBox, Mode=TwoWay, ElementName=W}" Width="300"/>
In the above example, I set on my Window Name="W".

WPF Databinding vs Programmatically generating controls

My underlying data structure consists of a hierarchy of objects that are built upon a common abstract class:
public abstract class model {
public string name {get;};
public string type {get;};
}
public class A:model {
public int val1 {get; set;};
}
public class B:model {
public int val2 {get; set;};
}
public class C:B {
public Dictionary<string, int> extra_props;
}
My goal is to create a UI that upon object selection, is able to dynamically preview but also be able to modify the object's underlying properties.
I'm an absolute noob regarding WPF, so I have no idea what its full capabilities are. For now regarding the single value properties, I have found my way to use Databinding to bind the properties to the UI elements. It works like a charm for showing and also modifying the values.
<GroupBox x:Name="InfoBox" Header="Object Information">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="41*"/>
<ColumnDefinition Width="200*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="50*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Grid.ColumnSpan="2" >
<TextBlock>Name:</TextBlock>
<TextBlock Text="{Binding name}" HorizontalAlignment="Right"></TextBlock>
</DockPanel>
<DockPanel Grid.Row="1" Grid.ColumnSpan="2" >
<TextBlock>Type:</TextBlock>
<TextBlock Text="{Binding type}" HorizontalAlignment="Right"></TextBlock>
</DockPanel>
<DockPanel Grid.Row="2" Grid.ColumnSpan="2" >
<TextBlock>Material Name:</TextBlock>
<TextBlock Text="{Binding val1}" HorizontalAlignment="Right"></TextBlock>
</DockPanel>
</Grid>
</GroupBox>
Problem 1:
When an object of type B is bound, property val1 doesn't exist and I'm getting some BindingErrors in the debug output. These cause no problem during execution at all, but I have found no way of catching them and returning a default value or something.
The only way that I have thought of for solving this issue is to add all the desired properties in the abstract class so that they all exist on all derived classes with some null values or something, but this doesn't feel right at all.
Problem 2:
For more complex classes like C, I want to dynamically generate the UI based on a set/list of properties of the class. For now I have absolutely no idea how to do it with databinding, except for adding them all one by one in the XML and working around the issues of problem 1.
The most viable solution that I thought of is to programmatically generate a Control and add it to the main window with textboxes and inputs for the class properties that I need, and again programmatically hopefully be able to bind the object to the control so that the values are read/set appropriately. Obviously this method would resolve problem 1 as well.
I'm not even sure if this solution is possible or not, but in any case I need recommendations and advice on mainly if there is a way to resolve my issues with data-binding or if I should go with with programmatically updating the UI (if possible).
Well for a noob you're doing well so far :) There's actually a very simple solution to both of these problems: DataTemplates. You can read all about them on the microsoft site.
In your case you want to declare a separate template for each type you're trying to display:
<DataTemplate DataType="{x:Type local:A}">
<TextBlock Text="This object is type A">
</DataTemplate>
<DataTemplate DataType="{x:Type local:B}">
<TextBlock Text="This object is type B">
</DataTemplate>
...and so on. This is typically done in the Resources block of whatever window/user control that this code appears on, although it can also be declared in the App.xaml resources block etc.
DataTemplates are used by any WPF control that is able to bind to data in some way. Take Button, for example....the overall look of the button including the border and how it behaves to mouse-over etc is dictated by its ControlTemplate (which you can also override yourself of course). But at some point in the XAML it has to render the actual data that you've assigned to its "Content" property, and if you were to look at the ControlTemplate for Button you'd find something like this buried inside it:
<ContentPresenter />
This effectively says "ok, this is where my actual data should be rendered, but instead of declaring it specifically here I want you to refer to the object's corresponding DataTemplate instead". In this way you can create a top-level button style using a single ControlTemplate, but you can then specify multiple DataTemplates for each of the types of things that you'll render inside it.
Lots of controls use DataTemplate, including things like ListBox etc where each element in the list can be given a different graphical representation based on its type. Going back to your own specific problem, if you just want to render the data itself without any bells and whistles etc around it then just use a ContentControl:
<ContentControl Content="{Binding MyModel}" />
So MyModel should be a property of type model that's in your view model layer (or whatever else you've set the DataContext to). Assigning that property to be an instance of type A, B or C will cause the ContentControl to be populated with an instance of whatever you've declared in its DataTemplate.

Xamarin Forms Templated control - child event handling

I'm trying to implement the equivalent of a WPF lookless control using Xamarin Forms. That is:
a class that derives from ContentView
a Style which sets (implicitly) its ControlTemplate
a Template which contains its UI
I'm using this approach so that the control can expose some bindable properties which can be set by its user, properties to which then elements from its ControlTemplate TemplateBind. It's working great and the UI loads as expected.
However, I have now the problem of handling events from its UI children. For example, I have some grids in its ControlTemplate and I'd like to handle the Tapped event on them.
I tried the following:
Adding, in XAML, a <Grid.GestureRecognizers>, where TappedCommand is a command exposed by the control. This throws InvalidOperationException at app load with "invalid state" or something.
Same as above, but instead of using a Command, using an EventTrigger and a custom ExecuteCommandAction which inherits TriggerAction<View>. My ExecuteCommandAction's Invoke is never called.
Same as above, but using a normal Tapped event handler . This works, but the handler is in the App.xaml.cs, so not really helpful, I need it to be in the control itself.
What I can additionally think of trying is declaring x:Name on the Grid in the template and referencing it from the control, then programatically adding a TappedGestureRecognizer to it, but I don't know where to do this. In WPF there was an OnApplyTemplate() method where it was "safe" to look in the template for children by name. Is there something similar in Xamarin Forms?
Any other suggestions are appreciated, thanks a lot!
Later edit, here's the code:
public class NextPrevDateSelector : ContentView
{
public NextPrevDateSelector ()
{
this.ShowPreviousDayCommand = new Command(() =>
{
/// This is where I'd like to handle the taps on the
/// element (Grid or Button or whatever) inside the template
/// which should change the date to the previous day. This
/// need not be a Command, if I could register an event handler
/// to the Tapped event that'd also be fine
});
}
// These events would be subscribed to by users of the control.
// They'd probably be commands, not events, but I put events here to
// keep the code small.
public event EventHandler PreviousDaySelected;
public event EventHandler NextDaySelected;
// This property can be used by users of the control to customize how the text inside the control looks like, the elements in the control's template will TemplateBind to this
public static readonly BindableProperty TextStyleProperty = BindableProperty.Create(
propertyName: "TextStyle",
returnType: typeof(Style),
declaringType: typeof(NextPrevDateSelector),
defaultValue: null);
public Style TextStyle
{
get { return (Style)GetValue(TextStyleProperty ); }
set { SetValue(TextStyleProperty , value); }
}
public ICommand ShowPreviousDayCommand { get; private set; }
}
And here's the XAML in the App.xaml ResourceDictionary
<Style TargetType="dateSelector:NextPrevDateSelector">
<Setter Property="ControlTemplate" Value="{StaticResource DateSelectorTemplate}" />
</Style>
And the control template
<ControlTemplate x:Key="DateSelectorTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid x:Name="prevDayButton" Grid.Column="0" Padding="20,0,20,0" HorizontalOptions="Start">
<Label Grid.Column="0" Text="<" Style="{TemplateBinding TextStyle}" />
</Grid>
<Grid Grid.Column="1" HorizontalOptions="Center">
<Label Text="Today etc" Style="{TemplateBinding TextStyle}" />
</Grid>
<Grid x:Name="nextDayButton" Grid.Column="2" Padding="20,0,20,0" HorizontalOptions="End">
<Label Text=">" Style="{TemplateBinding TextStyle}" />
</Grid>
</Grid>
</ControlTemplate>
I'd need to know when prevDayButton, nextDayButton and the center button were tapped. As mentioned I tried this first:
<Grid x:Name="prevDayButton" Grid.Column="0" Padding="20,0,20,0" HorizontalOptions="Start">
<Label Grid.Column="0" Text="<" Style="{TemplateBinding TextStyle}" />
<Grid.GestureRecognizers>
<TapGestureRecognizer Command={TemplateBinding ShowPreviousDayCommand}/>
</Grid.GestureRecognizers>
</Grid>
This throws at app initialization. Next I tried this:
<Grid x:Name="prevDayButton" Grid.Column="0" Padding="20,0,20,0" HorizontalOptions="Start">
<Label Grid.Column="0" Text="<" Style="{TemplateBinding TextStyle}" />
<Grid.GestureRecognizers>
<TapGestureRecognizer>
<EventTrigger Event="Tapped">
<behaviors:ExecuteCommandAction/>
</EventTrigger>
</TapGestureRecognizer>
</Grid.GestureRecognizers>
</Grid>
...where ExecuteCommandAction is a TriggerAction<View>, but its Invoke method is never called. So I'm left with adding a normal event handler to the "buttons", but I can't add it in XAML as the handler needs to be in my control and I can't add it in my control because I can't find a way to get a reference to the buttons so that I can add a gesture recognizer to them.
I hope the code helps make the question clearer.

How to find the right item out of a Listbox bound to a CollectionViewSource

i have a ListBox with its ItemsSource bound to a CollectionViewSource, which is bound to an ObservableCollection.
The template for the ListBoxItem includes a CheckBox which, when checked, indicates that the item is selected.
My Problem is, that I have no idea how to find out which items have their CheckBox clicked.
What are the checkboxes bound to? If it is bound to a property on the objects in your collection, then you shouldn't need to figure out which Checkbox was clicked. If it is not bound to something on the object or ViewModel you might be able to get the SelectedItem from the listbox.
Previously I have bound the SelectedItem property of the listBox to a property on my ViewModel so that I can have things that run whenever it changes.
As to getting the index, you should be able to match the idex returned from the listbox with an index of an item in the CollectionViewSource.View which contains the current view of the collection in the order it is displayed.
If you are not using MVVM, I would suggest it. I started out not using it and quickly got mired in the code-behind.
Example in MVVM
Lets say we have MyClass with three string properties and a boolean. In MVVM, we have a MyClassViewModel which has a property to contain an instance of MyClass along with any needed functionality for the View (a listboxitem in this case). We also have a MyWindowViewModel which will hold the collection of data, and other stuff for our main view.
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
Example ViewModels
Public Class MainViewModel
Inherits ViewModelBase
Public Property MyClassCollection as New ObservableCollection(Of MyClassViewModel)
End Class
Public Class MyClassViewModel
Inherits ViewModelBase
Public Property ModelClass as MyClass
Public Sub New()
End Sub
Public Sub New(ByRef CustClass as MyClass)
ModelClass = CustClass
End Sub
End Class
When we get our data, we put it in the ObservableCollection(Of MyClassViewModel). I usually do this in the WorkCompleted handler for a data retrieval backgroundworker.
For Each mc as MyClass in e.Results
MyClassCollection.Add(New MyClassViewModel(mc)
Next
The listbox will still get it's items from the observable collection through the collectionViewSource, but now they will be of type MyClassViewModel.
<DataTemplate DataType="{x:Type local:MyClassViewModel}">
<Border BorderBrush="#FF036200" BorderThickness="1" Background="#FF3CC600" CornerRadius="10">
<Grid Height="Auto" Margin="4" DataContext={Binding ModelClass}>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*"/>
<ColumnDefinition Width="0.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.5*"/>
<RowDefinition Height="0.5*"/>
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding StringProp1}" VerticalAlignment="Top" Margin="0" FontSize="16"/>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding StringProp2}" VerticalAlignment="Top" Margin="0" Grid.Row="1" FontSize="16"/>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding StringProp3}" VerticalAlignment="Top" Grid.Column="1" Margin="0" FontSize="16"/>
<CheckBox Content="Is Bool True?" HorizontalAlignment="Left" IsChecked="{Binding BoolProp}" VerticalAlignment="Top" Grid.Column="1" Margin="0" Grid.Row="1" FontSize="16"/>
</Grid>
</Border>
</DataTemplate>
Thus when someone clicks the checkbox, it changes the value on the object underneath, and since the listbox item represents the ViewModel, if you want to change something on the ListBoxItem, databind it to a property on the ViewModel and change that property.
For instance, lets say you want to change the color of the ListBoxItem to a random color when a user checks the checkbox (and for whatever reason you don't want to use a trigger or something. You could create a property on the MyClassViewModel of type Brush and Databind the Border.Background to it, and a property of Boolean which sets the MyClass property to the same value. In the setter for the boolean property, you check the value, and if it is true, set the brush value (Random Brush generator not included).
This way the ViewModel tells the view how to display the data in the Model, and can intercept datachanges from the View and do something with it if necessary.
<DataTemplate DataType="{x:Type local:MyClassViewModel}">
<Border BorderBrush="#FF036200" BorderThickness="1" Background="{Binding BorderBackground}" CornerRadius="10">
Reply to Comment
Everyone kind of has their own way of doing MVVM. I have several different kinds of ViewModels that I use. Some are really Form View Models (Used to control the way a form works), Model View Models (used to tell the View [generally a usercontrol for editing details or a ItemsControl DataTemplate] how to display the data). With Form View Models, I sometimes break them down to NavigationViewModel and Record Maintenance ViewModels depending on the situation.
In this case I really have a ViewModel for controlling the form and a Viewmodel for displaying the data. The Form View Model often handles button commands for adding or removing items in a collection, or specifying logic that tells the View whether the save or other action button is enabled.
INotifyPropertyChanged
Very light ViewModelBase class implementing INPC
Imports System.ComponentModel
Public MustInherit Class ViewModelBase
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
End Class
And in the ViewModels that inherit:
Public Property IsSelected() As Boolean
Get
Return m_IsSelected
End Get
Set(ByVal value As Boolean)
m_IsSelected = value
OnPropertyChanged("IsSelected")
End Set
End Property

Categories