Stray Checkbox Checked and Unchecked Events for Windows Phone 7 - silverlight

I have a ListBox ItemTemplate where I have a CheckBox that is Binded to a List<> that I am maintaining in my application.
My XAML:
<ListBox x:Name ="lstTasks">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="30,12,0,0">
<CheckBox IsChecked="{Binding use}" Unchecked="CheckBox_Unchecked" Tag="{Binding key}" Checked="CheckBox_Checked" />
<TextBlock Width="160" Text="{Binding key}" Margin="20,18,0,0" />
<TextBlock Text="{Binding value}" Margin="0,18,0,0"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And :
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
lstTasks.ItemsSource = null;
lstTasks.ItemsSource = a.Words; //a.Words is the List<DataClass>
}
I have observed that the Checked and Unchecked events get fired on its own when I scroll in the list of these Items. I have logic attached to these Events and they mess the whole thing up by checking and unchecking random entries. This starts after I uncheck one of the items. (By default all the items are checked)
I am new to WPF and Windows Phone, what am I doing wrong.?

Firstly, Windows Phone does not use WPF. You are using Silverlight which is based on a sub-set of WPF. They are different technologies with different capabilities. Much of what you can do on one you can't do on the other.
It would help to see more of your code, particularly the viewmodel to get a complete picture of what's going on.
I suspect one of two things though:
1 - there is some logic somewhere based on the setting of the use property and as this is changed in the vm, the fact it is reflected in the UI and that triggers the events.
2 - You have a virtualized list inside your ListBox and as items are loaded it's triggering the UI updates (due to the binding) and that in turn is triggering the events.
Without being able to see more code it's hard to be more specific.
As an alternative to this approach you could use a 2-way binding of the use property and move the logic from your event handlers (for checked and unchecked) and into the property setter.

Related

Set WPF CheckBox in a ComboBox to Checked

I have a custom WPF control - essentially a combo-box with check-boxes. The combo-boxes are successfully bound to a list of available items.
This control is to represent a field on a form. It is possible for the form to be completed already, in which case, the control must be rendered with the selected items - i.e. any items previously selected must be rendered as a checked CheckBox; it is here I'm running into trouble.
My first thought was to simply bind the IsChecked property - I don't think this can be done as the list of currently-selected-items is different from the list of available items which the ComboBox is bound to.
Basically, how do I gain access to the CheckBox object to set the Checked property to true? I've looked into this extensively and I can't fathom this out.
I'm using the ItemContainerGenerator approach - so when the user clicks on the drop-down, it is intended that a handler will iterate through the CheckBoxes and set the relevant boxes to checked.
Here's the XAML:
<ComboBox x:Name="FieldOptions"
ItemsSource="{Binding}"
HorizontalAlignment="Stretch"
Height="30"
KeyDown="FieldOptions_OnKeyDown">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="checkbox"
Content="{Binding Path=Text}"
Uid="{Binding Path=ID}"
FontStyle="Normal"
Foreground="Black"
Checked="CheckBox_OnChecked" />
</DataTemplate>
</ComboBox.ItemTemplate>
Any thoughts would be much appreciated.

Losing the binding for radiobutton after I set the property manually in code

I have a list on my WPF xaml which contains two items. Below is the Style template for each item. Now on UI this shows like a group of radio buttons(No. of radio buttons depends on no. of items in my list).
<Style x:Key="RadioButtonListBoxItemStyle" TargetType="{x:Type ListBoxItem}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<RadioButton FlowDirection="LeftToRight"
Margin="10 15"
Content="{Binding Value}"
GroupName="{Binding DisplayGroupName}"
IsChecked="{Binding IsSelected, Mode=TwoWay}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now I bind a list(having 2 items) using the above style template to get two radio buttons. What happens is everything works pretty fine i.e when I change the selection of radio button on UI the IsSelected property is getting updated properly to true or false depending on whether my radio is checked/un-checked. But if I try to set the list in the code manually, then from that point my binding of the radio button's with my list is lost and nothing happen's.
Any help on this would be great and based on my needs I have to set the list in the code manually. So is there any solution in a way that binding will not be lost even though I set the list in my code manually. Thanks.
-Ady.
This is a common problem with radio buttons in WPF, and it has to do an unusual aspect of binding, one that is marginally more feature than bug.
The design of binding assumes that the only two things that change the value of a binding's target property are a) actions in the UI and b) changes to the source property. If you set the target property of a binding in code - like, you explicitly set the Background of a Border, even though it has a binding - the binding decides that you know what you're doing, and that it should just get out of the way. So it turns itself off.
This is a pretty sensible design decision, for the most part. It's better than throwing an exception, for instance. Most of the time, you're not going to ever set IsEnabled in code anyway; you'll let the binding do it. Especially if you're using MVVM.
Okay, so what happens if you have radio buttons in a group?
When you check one button in the group, the WPF code that manages radio button groups unchecks all the other buttons in the group, by setting IsChecked to false in code. The binding disables itself. Oops.
Here's the solution: If you're using radio buttons and binding, don't use groups. Handle the mutual exclusion logic in your view model code. In your case, code your view models so that only one object in a collection can have IsSelected true at any given time. (Yes, this is a pain.)
The radio buttons will still work as expected, but since the only properties being set by code are the source properties, binding won't break.
you are setting the style for the listboxitem class, including the bindings. so, when you set the list from code behind it does not contain listboxitems, it contains the items from your list. so, the style does not apply. what you should do is make the <DataTemplate> for the type of item in your list--in effect telling WPF what you want each item to look like.
<DataTemplate TargetType="{x:Type MyCustomClass}" >
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Deleteable, Mode=TwoWay}" />
<Label Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</DataTemplate>
(this is off the top of my head, so the xaml might not be exactly right)

Two-way databinding to ObservableCollection on Silverlight TreeView using DragDropTarget

Here's the question at its most basic: how do I listen for an update of what is changing in a TreeView control modified via a DragDropTarget?
So here's my deal: I have a TreeView that holds agenda items. All are of the same data type (WCFAgendaItem), and are loaded into a hierarchy with children expressed as a property ChildItems. The whole thing is wrapped up in an ObservableCollection and bound to the TreeView using MVVM Light. Works great to view. I also want users to be able to use drag and drop to reorder, reorganize and add new items to this agenda coming from a variety of other sources (one example is a ListView of image slides). All new items would also have the same data type of WCFAgendaItem, for consistency's sake and easy serialization.
Here's my issue: dragging and dropping works beautifully on the UI using the Toolkit's drag drop functionality. But I have no idea how to get the ViewModel to understand changes to the contents of the TreeView.
Code from the view (Agenda.xaml):
(up top)
<UserControl.Resources>
<AHHSTeam_SLClassroomManagerMVVM_Helpers_Converters:BooleanVisibilityConverter x:Key="BooleanVisibilityConverter"/>
<sdk:HierarchicalDataTemplate x:Key="hdtAgenda" ItemsSource="{Binding ChildItems, Mode=TwoWay}" >
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ImageThumbnailWidth}" />
<ColumnDefinition Width="250" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding ThumbnailURL}" Width="{Binding ImageThumbnailWidth}" Height="{Binding ImageThumbnailHeight}" Visibility="{Binding HasImage, Converter={StaticResource BooleanVisibilityConverter}}" >
<ToolTipService.ToolTip>
<Image Source="{Binding ResizedImageURL}" />
</ToolTipService.ToolTip>
</Image>
<TextBlock Grid.Column="1" Text="{Binding Title}" TextWrapping="Wrap" />
</Grid>
</sdk:HierarchicalDataTemplate>
<Style TargetType="sdk:TreeViewItem" >
<Setter Property="IsExpanded" Value="True" />
</Style>
</UserControl.Resources>
(later on)
<controlsToolkit:TreeViewDragDropTarget Grid.Row="1" Grid.Column="0" x:Name="ddtAgenda" AllowDrop="True"
HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" >
<sdk:TreeView Width="375" ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Visible" ItemsSource="{Binding DailyAgenda, Mode=TwoWay}" ItemTemplate="{StaticResource hdtAgenda}">
</sdk:TreeView>
</controlsToolkit:TreeViewDragDropTarget>
ViewModel code (AgendaViewModel.cs) --> I tried listening for CollectionChanged, so far that doesn't seem to work
(in constructor)
//add notification of agenda changes
DailyAgenda.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(DailyAgenda_CollectionChanged);
(event)
void DailyAgenda_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
System.Windows.MessageBox.Show("Daily agenda updated, now has " + e.NewItems.Count.ToString() + " top-level elements.");
}
Code from model (WCFAgendaItem.cs)
[ContentProperty("ChildItems")]
public partial class WCFAgendaItem: INotifyPropertyChanged
{
private ObservableCollection<WCFAgendaItem> _childItems = new ObservableCollection<WCFAgendaItem>();
public ObservableCollection<WCFAgendaItem> ChildItems
{
get
{
return _childItems;
}
set
{
_childItems = value;
}
}
...
I am pretty sure that I get that listening for CollectionChanged isn't right in any case, given that this data doesn't just change at the top level. I looked at EventToCommand in Blend (MVVM Light, remember) but the only TreeView-specific event appears to be SelectionChanged, which doesn't seem right either. I looked at putting an EventToCommand trigger on the TreeViewDragDropTarget, but aren't those methods about overriding how the UI interactions happen? I don't think INotifyPropertyChanged on WCFAgendaItem is right for this either: although I'm going to want that later for editing item titles, it doesn't seem like it'll help me when items get moved around.
Maybe what I'm looking for is a stretch, but what I really want to have happen is for Silverlight to understand that the databinding works both ways on the ordering and contents of the WCFAgendaItem collection, and do all the collection reworking itself based on UI interactions. Then I could just listen for an update event after the collection is reworked - after that I can just crawl the modified ObservableCollection bound to the TreeView, and flatten/serialize/update via WCF.
Failing the ideal situation: I'm willing to crawl TreeViewItems if need be, but even if that's what I need to do I'm stuck on when to do it. Plus I need a way to pass all that back to the ViewModel so I'm not writing code behind. Do I need to attach to Drop() and rework the dropping logic? I found several old articles about custom drag drop implementations starting from the Toolkit, but nobody mentions how to save out the modified TreeView, especially in an MVVM situation.
finally {
While typing this out I found this article which may be useful, though that's a fair amount of work in the ViewModel. This is promising and I'll investigate, but I'm still holding out hope for something simpler. Also it looks like the Toolkit events have changed a little since the article was written.
}
I also had issues implementing this type of DragDrop functionality. The root cause seemed to be that neither the ItemDragCompleted event (EventHandler) nor ItemDroppedOnSource (DragEventHandler) pass the index at which the item was dropped.
I ended up subclassing the DragDropTarget in order to expose the protected method:
int GetDropTargetInsertionIndex(TItemsControlType dropTarget, DragEventArgs args)
I then used an attached behavior to assume responsibility for inserting items at the specified index into to the underlying collections.
I'm afraid the underlying code is too expansive to include in a StackOverflow answer (mainly due to extensive decoupling) but it's on my list of subjects to blog about. In the mean time, I hope the information above helps, it was certainly the key to the solution for me.
Ian

SL ItemsControl, command on ViewModel not firing from ItemsControl (CheckBox)

I'm using PRISM v2, CAL, SL4 and MVVM and have a delegate command on my ViewModel called CheckCommand. The ItemsControl contains a checkbox and I'm trying to get the items in ItemsControl/Checkbox to fire this command when it's checked - but it's not communication back to the viewmodel!
I think it's because each items 'datacontext' is the individual object the item is bound to, rather than the ViewModel?
- My suspicion is actually correct, cause if I move my DelegateCommand out of the viewmodel and into the class defining the items in itemscontrol I can see the commands/methods beeing fired!
View:
<ListBox x:Name="BasketListBox" ItemsSource="{Binding BasketCollection}" MinWidth="200">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox commands:Checked.Command="{Binding CheckCommand}" IsChecked="False" </CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
Can anyone point me in the right direction please?
Cheers, Mcad.
EDIT 1:
The commanding now works, see solution below. BUT, I now run into another problem:
"An exception occurred while creating a region with name 'basketRegion'. The exception was: System.InvalidOperationException: ItemsControl's ItemsSource property is not empty. This control is being associated with a region, but the control is already bound to something else. If you did not explicitly set the control's ItemSource property, this exception may be caused by a change in the value of the inherited RegionManager attached property"
Created seperate question for this problem to make it more clean:
PRISM-MVVM, ItemsControl problem with View injection
You want every CheckBox to fire the same command? You could:
<CheckBox commands:Checked.Command="{Binding DataContext.CheckCommand, ElementName=BasketListBox}"
Or you could have every child view model expose the command via their own property.
Thanx Kent. You put me on the right path to solve this, ended up doing this:
<ListBox x:Name="basketListBox" ItemsSource="{Binding basketcollection}" MinWidth="200">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox commands:Checked1.Command="{Binding DataContext.CheckCommand, ElementName=basketListBox}" Content="{Binding basketName}"> </CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>

Databinding Not Updating When Using {Binding .} or {Binding}

I have an ObservableCollection of addresses that I am binding to a ListBox. Then in the ItemTemplate I am Binding to the current address record using {Binding .}. This results in my addresses displaying using their ToString method which I have setup to format the address. All is good, except if I update properties on an individual address record the list in the UI does not update. Adds/Deletes to the list do update the UI (using the ObservableCollection behavior). If I bind directly to properties on the address the UI does update (using the INotifyPropertyChanged behavior of the Address object).
My question is, is there a way to notify the UI of the change to the object as a whole so that I can still use this syntax or do I need to punt and put a DisplayText property on my address type that calls the ToString method and bind to that? FYI, this is an MVVM architecture so I don't have the luxury of calling Refresh on the ListBox directly.
Thanks for any help/ideas.
<ListBox x:Name="AddressList" ItemsSource="{Binding Addresses}" Background="Transparent" BorderBrush="Transparent"
Width="200" HorizontalAlignment="Left">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding .}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When you bind to the Address object itself, the object itself -- that is, its identity -- doesn't change, even though its properties do. WPF therefore doesn't know to refresh the binding in this case.
So yes, you need to bind to a notifying property (or properties) rather than the whole object. As you say, one way to do this is to create a DisplayText property, and raise the PropertyChanged event for that property whenever something that affects the display text changes. Another is to use multiple TextBlocks in a horizontally oriented StackPanel, each bound to a particular property e.g.
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding HouseNumber}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding Street}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding City}" />
</StackPanel>
The advantage of the second approach is that it gives you flexibility in the UI to change how addresses are displayed, e.g. multiple lines, formatting, etc.; the downside is that it gets complicated if you have conditional logic e.g. an optional flat number or second address line.
I tried to reproduce the problem and succeeded.
I activated the step-into-.NET debugging options, and saw that WPF does not listen to INotifyPropertyChanged if the path in the binding is empty.
What worked to get a change to be reflected in the list box is to replace the whole object in the ObservableCollection. This triggers the INotifyCollectionChanged, with the Replace action.
But this may not be acceptable in your case. And it could be seen more like a hack than a solid solution.
I'd seriously consider having a DataTemplate for Address. There you should bind to the exact properties you need (which would create the listener for INotifyPropertyChanged). It is more flexible than ToString() and you may encounter cases where you have a need for ToString() to do something for non-UI stuff, which would create a conflict. And honestly, ToString is not really meant for UI stuff.

Resources