ComboBox Item Selection Works With Keyboard But Not Mouse - c#

I have a combo box that is not working as I expect at runtime. I can use the mouse to expand the drop-down window, but clicking an item does not seem to select it. The dropdown goes away, but the selection is not changed. The same control seems to work as expected using the keyboard. Arrow up/down changes the selection. I can use the arrow keys to choose and enter to select to change the value as well.
How do I get clicking to select an item?
<DataTemplate DataType="{x:Type myType}">
<Border ...>
<Grid x:Name="upperLayout">
<Grid x:Name="lowerLayout">
<ComboBox x:Name="combo"
Grid.Column="2"
ItemsSource="{Binding Things}"
SelectedItem="{Binding SelectedThing}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Grid>
</Border>
</DataTemplate>

I can't really tell what's wrong from your code however, I'd strongly suggest you to use Snoop to debug your controls (http://snoopwpf.codeplex.com/)
By holding Ctrl+Shift and pointing the mouse where you ComboBox is supposed to grab the input you would instantly find out who is having the focus instead of your combo box.
You can even change the value of a property, really your best friend for debugging your templates !
EDIT
I'm afraid but the code you've posted works for me:
<Window x:Class="WpfApplication6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication6="clr-namespace:WpfApplication6"
Title="MainWindow"
Width="525"
Height="350">
<Window.Resources>
<DataTemplate x:Key="myTemplate" DataType="{x:Type wpfApplication6:MyType}">
<Border>
<Grid x:Name="upperLayout">
<Grid x:Name="lowerLayout">
<ComboBox x:Name="combo"
Grid.Column="0"
ItemsSource="{Binding Path=Things}"
SelectedItem="{Binding Path=SelectedThing}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type wpfApplication6:MyThing}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
<Grid x:Name="grid">
<ContentControl x:Name="content" ContentTemplate="{StaticResource myTemplate}" Margin="58,79,71,40" />
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
MyType type = new MyType()
{
Things = new List<MyThing>() {new MyThing() {Name = "aaa"}, new MyThing() {Name = "bbb"}}
};
content.Content = type;
}
}
public class MyType
{
public MyThing SelectedThing { get; set; }
public List<MyThing> Things { get; set; }
}
public class MyThing
{
public string Name { get; set; }
}
Maybe something else is screwing it such as a style with no key or whatever, post more of your code you're having a problem with.

Root cause was that another developer had implemented some code that changed the focus on the preview mouse down event. This code was updated to have the desired behavior without modifying focus and the combo box now works as expected. The information needed to diagnose was not in the original question (can't publish it all...).

Related

UserControl with datatemplated ContentControl throws binding errors when its DataContext changes

In my project, I wanted a UserControl that can display different formats of an image (bitmap, svg), selected from a ListBox. The SelectedItem of the ListBox is bound to the appropriate view model, which in turn changes the DataContext of the UserControl, and what I want to achieve is for it to change the displaying control (an Image for bitmaps, a SharpVectors.SvgViewBox for svg files) through data templates. It does so, but raises data binding errors, as if the templates were still intact whilst the UserControl's DataContext has already been changed.
I should like to a) avoid any data binding errors even if they cause no visible problems b) understand what is happening, so I prepared a MWE, which, to my surprise, displays the same behaviour, so I can present it here.
My minimal UserControl is as follows:
<UserControl x:Class="BindingDataTemplateMWE.VersatileControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:system="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate DataType="{x:Type system:String}">
<TextBlock Text="{Binding Content.Length, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentControl}}" />
</DataTemplate>
<DataTemplate DataType="{x:Type system:DateTime}">
<TextBlock Text="{Binding Content.DayOfWeek, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentControl}}" />
</DataTemplate>
</UserControl.Resources>
<Grid>
<ContentControl
Content="{Binding .}" />
</Grid>
</UserControl>
The MainWindow that references this UserControl has the following XAML:
<Window x:Class="BindingDataTemplateMWE.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BindingDataTemplateMWE"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox
Grid.Column="0"
SelectedItem="{Binding Selected}"
ItemsSource="{Binding Items}" />
<local:VersatileControl
Grid.Column="1"
DataContext="{Binding Selected}" />
</Grid>
</Window>
with the following code-behind (to make the MWE indeed minimal, I made the window its own DataContext, but originally there is a dedicated view model):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
namespace BindingDataTemplateMWE
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private object selected;
public event PropertyChangedEventHandler PropertyChanged;
public List<object> Items { get; }
public object Selected {
get { return selected; }
set {
selected = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Selected)));
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
Items = new List<object>() { "a string", DateTime.Now, "another string" };
}
}
}
When I select a different item in the list, the desired effect takes place: the UserControl displays the length if a string is selected and the day of week when a DateTime. Still, I get the following binding error when selecting a DateTime after a string:
Length property not found on object of type DateTime.
and conversely, selecting a string after a DateTime yields
DayOfWeek property not found on object of type String.
It is clear that what I am doing is not meant to be done, but I do not know what the correct paradigm is and what happens in the background. Please advise me. Thank you.
I've seen this problem often when creating complex data templates (several levels of nesting) when views are loaded/unloaded. Honestly, some of such errors I am ignoring completely.
In your case something similar happens because you are manipulating DataContext directly. At the moment the new value is set, the previous value is still used in bindings, which monitor for source change and will try to update the target.
In your scenario you don't need this constant monitoring, so an easy fix is to use BindingMode.OneTime:
<DataTemplate DataType="{x:Type system:String}">
<TextBlock Text="{Binding Content.Length, RelativeSource={RelativeSource AncestorType=ContentControl}, Mode=OneTime}" />
</DataTemplate>

Show User Control View inside Tab Item when Tab Item is selected

I have created a couple of UserControl views and now I want to show the corresponding view when a tab item is clicked. So one tab item gets one view. I would like to do this in MVVM but don't know how.
Please take a look at the following code and give me some advice on how to achieve that:
The MainView (with the TabControl only):
...
<TabControl Name="pnlFormButtons"
Margin="25"
Background="Black"
SelectedItem="{Binding SelTab}"
>
<TabItem Name="tabInventurartikel" Header="Inventurartikel hinzufügen"
Background="BlanchedAlmond" Foreground="Black"
FontFamily="Verdana"
BorderBrush="Black"
>
</TabItem>
<TabItem Name="tabSonderartikel" Header="Sonderartikel hinzufügen"
Background="BlanchedAlmond" Foreground="Black"
FontFamily="Verdana"
BorderBrush="Black"
BorderThickness="2">
</TabItem>
<TabItem Name="tabAnlegen" Header="Lieferschein anlegen"
Background="BlanchedAlmond" Foreground="Black"
FontFamily="Verdana"
BorderBrush="Black"
BorderThickness="2"
IsEnabled="False">
</TabItem>
<TabItem Name="tabDrucken" Header="Lieferschein drucken"
Background="BlanchedAlmond" Foreground="Black"
FontFamily="Verdana"
BorderBrush="Black"
BorderThickness="2"
IsEnabled="False">
</TabItem>
<TabItem Name="tabHilfeseite" Header="Hilfeseite aufrufen"
Background="BlanchedAlmond" Foreground="Black"
FontFamily="Verdana"
BorderBrush="Black"
BorderThickness="2"
IsEnabled="False">
</TabItem>
<TabItem Name="tabFehlerMelden" Header="Fehler bzw. Bug melden"
Background="BlanchedAlmond" Foreground="Black"
FontFamily="Verdana"
BorderBrush="Black"
BorderThickness="2"
IsEnabled="False">
</TabItem>
</TabControl>
...
The MainViewModel (only relevant code):
...
//Binding Property SelTab - It binds to the selected tab item
private string _selTab;
public string SelTab
{
get { return _selTab; }
set
{
_selTab = value;
OnPropertyChanged("SelTab"); //INotifyPropertyChanged
GetSelTab(); //check which tab item is selected and display the corresponding view
}
}
public void GetSelTab()
{
UserControl usc = null; //initialize user control object
switch(SelTab) //which tab item is selected?
{
case "tabInventurartikel": // = TabControl.SelectedItem
usc = new Inventurartikel(); //Initialize (Show) Inventurartikel.xaml
SelTab.Content = usc; //Here I don't know how to actually show the view in the tab item because SelectedItem.Content does not exist...
break;
case "tabSonderartikel":
usc = new neuerArtikel(); //same problem here...
break;
default:
break;
}
}
...
NOTE:
The views for the tab items are basically just user control forms that I want to show inside the tab item when the corresponding tab item is selected.
I shouldn't post them here because I want to keep the focus on the actual problem as simple and as clear as possible. Any help is highly appreciated!
The easiest solution would be to bind the tab control's item source to a list of view models. Then, if you add/remove view models, tabs are added/removed accordingly.
Main window xaml:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="CustomHeaderTemplate">
<Label Content="{Binding TabName}" />
</DataTemplate>
</Grid.Resources>
<TabControl x:Name="tbCtrl" ItemsSource="{Binding Items}" Loaded="tbCtrl_Loaded" SelectionChanged="tbCtrl_SelectionChanged" ItemTemplate="{StaticResource CustomHeaderTemplate}">
<TabControl.ContentTemplate>
<DataTemplate>
<uc:DeviceTab/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
The important thing is the binding of ItemSource.
Tab control view model:
class TabControlViewModel
{
public ObservableCollection<ItemViewModel> Items { get; } = new ObservableCollection<ItemViewModel>();
}
Tab control code behind Loaded event. Here you can add view models and the tab control sets up the tabs accordingly:
private void tbCtrl_Loaded(object sender, RoutedEventArgs e)
{
var tabControlViewModel = new TabControlViewModel();
tabControlViewModel.Items.Add(new ItemViewModel());
DataContext = tabControlViewModel;
tbCtrl.SelectedIndex = 0;
}
This only works if all tabs are the same. There's also a solution if you need different user controls for each tab. In that case, you need to specify a data template for the tab item's content. Basically you can tell it to load user controls based on the type of the view model. Unfortunately I don't know how to do that, but I've seen examples for it. I know it's not the exact answer you need, but I hope it helps!

Button content does not change in real time

I have a button. I want it to be a favorite toggle button inside a listbox. See code below:
<Page
x:Class="W.Pages.ExPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Workout_EF.Pages"
xmlns:converter="using:W.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:data="using:W.Model"
mc:Ignorable="d">
<Page.Resources>
<converter:FavoriteValueConverter x:Key="favoriteConverter" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListBox Name="MyListbox"
SelectionMode="Single"
ItemsSource="{x:Bind exs}"
SelectionChanged="MyListbox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="data:Ex">
<StackPanel Orientation="Horizontal">
<Button Name="IsFavoriteToggle" Click="IsFavoriteToggle_Click">
<Button.Content>
<TextBlock
x:Name="isFavoriteTextBlock"
Text="{x:Bind IsFavorite, Converter={StaticResource favoriteConverter}}"
FontFamily="Segoe MDL2 Assets"/>
</Button.Content>
</Button>
<TextBlock
VerticalAlignment="Center"
FontSize="16"
Text="{Binding ExName}"
Margin="20,0,0,0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Page>
My problem is when I hit this button it does not change the icon in it (from emtpy star to full star and vice versa) in real time.
If the listbox will be loaded again the correct icon is displayed.
The code behind is:
namespace W.Pages
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class ExPage : Page
{
ObservableCollection<Exs> exs = new ObservableCollection<Exs>();
public ExPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
List<Exs> tmpEx = e.Parameter as List<Exs>;
foreach (Exs item in tmpEx)
{
exs.Add(item);
}
}
private void IsFavoriteToggle_Click(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
int index = MyListbox.Items.IndexOf(button.DataContext);
Ex ex = (Exs)MyListbox.Items[index];
DAL.SetToFavorite(ex, !ex.IsFavorite);
}
}
}
I noticed that there is some problem with the itemsource maybe. It needed to change its content after hitting the button. But I am not sure.
This is a common mistake that everyone make and that is ObservableCollection<T> is actually informing the binders about the changes in the collection not the objects in the collection.
You have a IsFavorite property in your Esx class that the button need to know about changes but for that, Esx needs to implement INotifyPropertyChanged
See this and if you need more help post the code for Esx class maybe we can help.
As Emaud said, you have to implement the INotifyPropertyChanged interface in your Esx class. And you also have to set set the Mode to OneWay in your binding because for x:Bind the default mode is OneTime so it does NOT listen to any changes.
Text="{x:Bind IsFavorite, Mode=OneWay, Converter={StaticResource favoriteConverter}}"

SelectedIndex with OneWayToSource binding does not trigger

I have a problem with a particular xaml databinding.
I have two listboxes (master-details, so the listboxes have IsSynchronizedWithCurrentItem set to true). I want my viewmodel to know when the selected item on the details listbox changes: I created an int property on my viewmodel class (i.e. we can call this property SelInd)and on the details viewmodel I bind this way:
SelectedIndex="{Binding Mode=OneWayToSource, Path=SelInd}"
I get no errors/exceptions at runtime, but the binding does not trigger: my viewmodel's property does not get updated when the selected item changes. If I change the binding mode to TwoWay everything works fine, but that's not what I need. I need it to work with OneWayToSource (btw the same non-working behaviour applies if I bind SelectedItem to SelectedValue properties).
Why do those bindings do not trigger with OneWayToSource?
Here's a more complete code example, just to get the things clearer:
EDIT: I can't show the real code (NDA) but I'll show here something simpler and similar enough (the Page's DataContext is an instance of the PageViewModel class explained later)
I just need that my viewmodel class's SelInd property should always reflect the value of SelectedIndex in the second ListBox. I have found alternative methods for doing this (Event handler in code-behind or an Attached Behaviour) but right now I'm just curious about WHY it doesn't work with OneWayToSource binding.
<Page>
<ContentControl x:Name="MainDataContext">
<Grid DataContext={Binding Path=Masters}>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0"
SelectionMode="Single"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding }">
<ListBox.ItemContainerStyle>
...
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Grid.Column="1"
SelectionMode="Single"
SelectedIndex="{Binding Mode=OneWayToSource, ElementName=MainDataContext,Path=DataContext.SelInd}"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Details}">
<ListBox.ItemContainerStyle>
...
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</ContentControl>
</Page>
Here's a sketch of the view model class
public class PageViewModel{
public ObservableCollection<MasterClass> Masters {get;set;}
public int SelInd {get;set;}
....
}
And here's MasterClass, it just holds a name and a list of details
public class MasterClass{
public ObservableCollection<DetailsClass> Details {get;set;}
public String MasterName {get;set;}
....
}
I think in your case, you must use the mode OneWay. By default, you have used mode TwoWay.
Quote from MSDN about TwoWay:
TwoWay binding causes changes to either the source property or the target property to automatically update the other. This type of binding is appropriate for editable forms or other fully-interactive UI scenarios. Most properties default to OneWay binding, but some dependency properties (typically properties of user-editable controls such as the Text property of TextBox and the IsChecked property of CheckBox) default to TwoWay binding. A programmatic way to determine whether a dependency property binds one-way or two-way by default is to get the property metadata of the property using GetMetadata and then check the Boolean value of the BindsTwoWayByDefault property.
Mode OneWay, that you need:
OneWay binding causes changes to the source property to automatically update the target property, but changes to the target property are not propagated back to the source property. This type of binding is appropriate if the control being bound is implicitly read-only. For instance, you may bind to a source such as a stock ticker or perhaps your target property has no control interface provided for making changes, such as a data-bound background color of a table. If there is no need to monitor the changes of the target property, using the OneWay binding mode avoids the overhead of the TwoWay binding mode.
Mode OneWayToSource:
OneWayToSource is the reverse of OneWay binding; it updates the source property when the target property changes. One example scenario is if you only need to re-evaluate the source value from the UI.
Below is a diagram for a better understanding of the:
Okay, then I'll show you an example that works for me. Perhaps it will be useful to you.
XAML
<Window x:Class="SelectedIndexHelp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SelectedIndexHelp"
Title="MainWindow" Height="350" Width="525"
ContentRendered="Window_ContentRendered"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<local:SelectedIndexClass x:Key="SelectedIndexClass" />
</Window.Resources>
<Grid DataContext="{StaticResource SelectedIndexClass}">
<ListBox x:Name="MyListBox"
BorderThickness="1"
Width="200" Height="200"
BorderBrush="#CE5E48"
DisplayMemberPath="Name"
Background="AliceBlue"
SelectedIndex="{Binding MySelectedIndex, Mode=OneWayToSource}" />
<Label Name="SelectedIndex" VerticalAlignment="Top"
Content="{Binding MySelectedIndex}"
ContentStringFormat="SelectedIndex: {0}"
Width="100" Height="30" Background="Lavender" />
</Grid>
</Window>
Code behind
public partial class MainWindow : Window
{
public class Person
{
public string Name
{
get;
set;
}
public int Age
{
get;
set;
}
}
private ObservableCollection<Person> DataForListBox = new ObservableCollection<Person>();
public MainWindow()
{
InitializeComponent();
}
private void Window_ContentRendered(object sender, EventArgs e)
{
DataForListBox.Add(new Person()
{
Name = "Sam",
Age = 22,
});
DataForListBox.Add(new Person()
{
Name = "Nick",
Age = 21,
});
DataForListBox.Add(new Person()
{
Name = "Cris",
Age = 25,
});
DataForListBox.Add(new Person()
{
Name = "Josh",
Age = 36,
});
DataForListBox.Add(new Person()
{
Name = "Max",
Age = 32,
});
DataForListBox.Add(new Person()
{
Name = "John",
Age = 40,
});
MyListBox.ItemsSource = DataForListBox;
MyListBox.Focus();
}
}
public class SelectedIndexClass
{
private int? mySelectedIndex = 0;
public int? MySelectedIndex
{
get
{
return mySelectedIndex;
}
set
{
mySelectedIndex = value;
}
}
}
Output
In this example, there is a class of data - Person, these data for ListBox. And the class SelectedIndexClass (DataContext), which contains the property MySelectedIndex, which is a parameter of binding OneWayToSource.
Edit: I'm glad you figured out with the problem. I'll try to explain by their example, why are you not working with ElementName case.
So, let's say we have this code:
<ContentControl x:Name="MainDataContext">
<Grid x:Name="MainGrid" DataContext="{StaticResource SelectedIndexClass}">
<ListBox x:Name="MyListBox"
BorderThickness="1"
Width="200" Height="200"
BorderBrush="#CE5E48"
DisplayMemberPath="Name"
Background="AliceBlue"
SelectedIndex="{Binding Path=DataContext.MySelectedIndex, Mode=OneWayToSource, ElementName=MainDataContext}" />
<Label Name="SelectedIndex" VerticalAlignment="Top"
Content="{Binding MySelectedIndex}"
ContentStringFormat="SelectedIndex: {0}"
Width="100" Height="30" Background="Lavender" />
</Grid>
</ContentControl>
As you probably understand, it will not work.
DataContext set on a specific node of the visual tree, all items below (in the visual tree) inherit it. This means that the DataContext will be working since the Grid and below the visual tree. Therefore, the following code will work:
<ContentControl x:Name="MainDataContext">
<Grid x:Name="MainGrid" DataContext="{StaticResource SelectedIndexClass}">
<ListBox x:Name="MyListBox"
BorderThickness="1"
Width="200" Height="200"
BorderBrush="#CE5E48"
DisplayMemberPath="Name"
Background="AliceBlue"
SelectedIndex="{Binding Path=DataContext.MySelectedIndex, Mode=OneWayToSource, ElementName=MainGrid}" />
<Label Name="SelectedIndex" VerticalAlignment="Top"
Content="{Binding MySelectedIndex}"
ContentStringFormat="SelectedIndex: {0}"
Width="100" Height="30" Background="Lavender" />
</Grid>
</ContentControl>
And also, it will work if the name of the point MyListBox. Usually, when set the DataContext, the element name is passed.
Well, I found a way to make it work. I just removed the data-context "indirection" so I don't have to use ElementName in my bindings, and it started working. The working xaml example is:
<Page>
<ContentControl >
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0"
SelectionMode="Single"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Masters }">
<ListBox.ItemContainerStyle>
...
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Grid.Column="1"
SelectionMode="Single"
SelectedIndex="{Binding Mode=OneWayToSource, Path=SelInd}"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Masters/Details}">
<ListBox.ItemContainerStyle>
...
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</ContentControl>
</Page>
Now, if someone knows exactly WHY the binding using ElementName does not work, I'd like to know it :)

Silverlight TimePicker / ChildWindow / DataTemplate combination causes UI freeze

I'm working on a Silverlight project, using MVVM, and I've run into a problem that only appears to occur under some fairly specific situations. I've tried to strip everything down as much as possible so only the important parts are left.
The Scenario:
A standard Silverlight ChildWindow
The ChildWindow has a Selector Control (e.g. a ComboBox or a ListBox).
The ChildWindow has a ContentPresenter
The Content of this ContentPresenter is bound to the SelectedValue of the above mentioned Selector Control (In reality it is bound to a ViewModel property, but for testing purposes this is not necessary).
The ContentPresenter uses a DataTemplateSelector to determine it's ContentTemplate
The Selector Control contains the string values "A" and "B" which correspond to DataTemplates "TemplateA" and "TemplateB".
"TemplateB" contains a TimePicker control.
The Problem:
After selecting "B" (therefore loading "TemplateB"), the next time you try to change the selected template, the host webpage will freeze. No exception is thrown and no information is given.
Notes:
Everything works fine outside of a ChildWindow.
Everything works fine if none of the templates contain a TimePicker control.
The TimePicker control appears to work fine if it is displayed without using a ContentPresenter/DataTemplateSelector.
Ive looked at TimePicker in ChildWindow causes an exception on the Silverlight Toolkit CodePlex page. That particular issue appears to be resolved and I've tried implementing the suggested workaround just to be sure, and it has no effect.
Code to reproduce the problem:
ChildWindow XAML:
<controls:ChildWindow
x:Class="TimePickerProblem.ChildWindow1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
xmlns:local="clr-namespace:TimePickerProblem"
Width="400"
Height="300"
Title="ChildWindow1">
<controls:ChildWindow.Resources>
<local:ViewModel
x:Key="vm" />
</controls:ChildWindow.Resources>
<Grid>
<Grid
x:Name="LayoutRoot"
Background="White"
DataContext="{StaticResource vm}">
<StackPanel
HorizontalAlignment="Center">
<ComboBox
x:Name="ComboBox"
Margin="20"
ItemsSource="{Binding Templates}" />
<ContentPresenter
Content="{Binding ElementName=ComboBox, Path=SelectedValue}">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<local:TemplateSelector
Content="{Binding}">
<local:TemplateSelector.TemplateA>
<DataTemplate>
<TextBlock
Text="Hello from A" />
</DataTemplate>
</local:TemplateSelector.TemplateA>
<local:TemplateSelector.TemplateB>
<DataTemplate>
<toolkit:TimePicker />
</DataTemplate>
</local:TemplateSelector.TemplateB>
</local:TemplateSelector>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</StackPanel>
</Grid>
<Button
x:Name="CancelButton"
Content="Cancel"
Click="CancelButton_Click"
Width="75"
Height="23"
HorizontalAlignment="Right"
Margin="0,12,0,0"
Grid.Row="1" />
<Button
x:Name="OKButton"
Content="OK"
Click="OKButton_Click"
Width="75"
Height="23"
HorizontalAlignment="Right"
Margin="0,12,79,0"
Grid.Row="1" />
</Grid>
ViewModel:
public List<string> Templates { get { return new List<string>() { "a", "b" }; } }
DataTemplateSelector:
public class TemplateSelector : DataTemplateSelector
{
public DataTemplate TemplateA { get; set; }
public DataTemplate TemplateB { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
string value = (string)item;
switch (value.ToLower())
{
case "a":
return TemplateA;
case "b":
return TemplateB;
default:
return base.SelectTemplate(item, container);
}
}
}
I've just tried in SL5, and couldn't reproduce your problem. If I've time, I will try SL4. Do you have any styles present that might cause a problem?
If you are describing a situation where overlay appears after dismissing ChildWindow, then it is known bug. Here's a workaround.
ChildWindow w = new MyChildWindow();
w.Closed += (s, eargs) => { Application.Current.RootVisual.SetValue(Control.IsEnabledProperty, true); };
w.Show();

Categories