I'm trying to validate a UI change when Enter key is pressed. The UI element is a textbox, which is data binded to a string. My problem is that the data binding hasn't updated TestText when Enter key is Up. It is only updated when I press the button which brings up a message box.
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window, INotifyPropertyChanged
{
String _testText = new StringBuilder("One").ToString();
public string TestText
{
get { return _testText; }
set { if (value != _testText) { _testText = value; OnPropertyChanged("TestText"); } }
}
public Window1()
{
InitializeComponent();
myGrid.DataContext = this;
}
private void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void onKeyUp(object sender, KeyEventArgs e)
{
if (e.Key != System.Windows.Input.Key.Enter) return;
System.Diagnostics.Trace.WriteLine(TestText);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(TestText);
}
}
Window XAML:
Window x:Class="VerificationTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" KeyUp="onKeyUp"
TextBox XAML:
TextBox Name="myTextBox" Text="{Binding TestText}"
Button XAML:
Button Name="button1" Click="button1_Click"
In order to force the TextBox to commit the value back to the binding source you can do:
var binding = myTextBox.GetBindingExpression(TextBox.TextProperty);
binding.UpdateSource();
Optionally, you can configure the binding to update the source every time the Text property changes which would mean on every character you enter into the text box.
<TextBox Name="myTextBox"
Text="{Binding TestText, UpdateSourceTrigger=PropertyChanged}" />
But this will raise a lot of property change notifications. What I have done in my applications was to create a class derived from TextBox to override the OnKeyDown method and when enter is pressed, I call the UpdateSource method as I described above and also call SelectAll on the TextBox to give the user an idea that I just "accepted" their input. Deriving a class from TextBox will let you reuse that behavior anywhere else you might want it in your application.
Related
I'm looking to synchronize between a text in the textbox and string in a variable. I found how to get the index in which the string was changed (in the textbox), the length added and length removed, but how can I actually find the string added?
So far I've used TextChangedEventArgs.Changes, and got the properties of the items in it (ICollection).
I'm trying to create a password box in which I could show the actual password by a function. hence I do not want the textbox to synchronize directly (for example, in the textbox would appear "*****" and in the string "hello").
If you want only text added you can do this
string AddedText;
private void textbox_TextChanged(object sender, TextChangedEventArgs e)
{
var changes = e.Changes.Last();
if (changes.AddedLength > 0)
{
AddedText = textbox.Text.Substring(changes.Offset,changes.AddedLength);
}
}
Edit
If you want all added and remove text you can do this
string oldText;
private void textbox_GotFocus(object sender, RoutedEventArgs e)
{
oldText = textbox.Text;
}
string AddedText;
string RemovedText;
private void textbox_TextChanged(object sender, TextChangedEventArgs e)
{
var changes = e.Changes.Last();
if (changes.AddedLength > 0)
{
AddedText = textbox.Text.Substring(changes.Offset, changes.AddedLength);
if (changes.RemovedLength == 0)
{
oldText = textbox.Text;
RemovedText = "";
}
}
if (changes.RemovedLength > 0)
{
RemovedText = oldText.Substring(changes.Offset, changes.RemovedLength);
oldText = textbox.Text;
if (changes.AddedLength == 0)
{
AddedText = "";
}
}
}
DataBinding is the most common way in WPF to show and collect data in a UI
Try this:
<Window x:Class="WpfApp3.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:WpfApp3"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<TextBox Text="{Binding Path=SomeText, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left"
Margin="101,83,0,0"
VerticalAlignment="Top"
Width="75" />
<TextBlock Text="{Binding SomeText}"
HorizontalAlignment="Left"
Margin="101,140,0,0"
VerticalAlignment="Top"
Width="75" />
</Grid>
</Window>
Code for the window:
public partial class MainWindow : Window
{
private readonly AViewModel viewModel = new AViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = viewModel;
}
}
And the code for the ViewModel that holds the data you want to show and collect:
public class AViewModel : INotifyPropertyChanged
{
private string someText;
public string SomeText
{
get
{
return someText;
}
set
{
if (Equals(this.someText, value))
{
return;
}
this.someText = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(
[CallerMemberName]string propertyName = null)
{
this.PropertyChanged?.Invoke(
this,
new PropertyChangedEventArgs(propertyName));
}
}
Although this looks complicated for a simple scenario it has a lot of advantages:
You can write automated (unit)test for the ViewModel without creating a UI
Adding extra fields and logic is trivial
If the UI needs to change, the ViewModel will not always need to change
The core of the mechanism is the {Binding ...} bit in the Xaml that tell WPF to synchronize the data between the Text property of the TextBox and the SomeText property of the object that is assigned to the DataContext.
The other significant bits are:
- in the constructor of the window the setting of the DataContext and
- in the ViewModel the raising of the PropertyChanged event when the SomeText property changes so the binding will be notified.
Note that this is just a basic example of DataBinding, there are many improvements that could be made in this code.
I have a problem binding to a WPF form . I have my own static "settings" class (singleton) that implements PropertyChangedEventHandler and raises the event whenever a property is updated.
The singleton object is added to resources in the form's constructor and the property is correctly read on form's initialization, thus suggesting that the binding is correct.
However, WPF does NOT register any event handler for PropertyChangedEventHandler and PropertyChanged is always null. Thus the event is never raised, and my form is never updated (it's meant to be updated on a button click).
What am I doing wrong?
I suspect that calling Resources.Add for some reason prevents WPF from registering its own event handler, but I'm not sure.
I've read multiple SO questions on similar topics, but the 2 most common issues are not creating a proper singleton (thus passing another instance to xaml then intended) or not implementing INotifyPropertyChanged. I'm doing both of these correctly.
Expected behavior:
Settings.TextValue is the property I'm interested in. In its setter, NotifyPropertyChanged is called, which unfortunately fails to raise this.PropertyChanged event, since WPF registers no handler.
When MainWindow.Button1 is click, the textBox's value is supposed to change to "ButtonA OK" from the initial value of Settings.TextBox ("testOK").
Here's the code:
Settings.cs:
namespace bindings
{
public sealed class Settings : INotifyPropertyChanged
{
private static readonly Settings instance = new Settings();
private Settings()
{
}
public static Settings Instance { get { return instance; } }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName = null)
{
// passing propertyName=null raises the event for all properties
if (PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string textValue = "testOK";
public static string TextValue
{
get { return Instance.textValue; }
set { Instance.textValue = value; Instance.NotifyPropertyChanged(); }
}
}
MainWindow.xaml.cs
namespace bindings
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
Resources.Add("foobar", Settings.Instance);
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
private void button1_Click(object sender, RoutedEventArgs e)
{
int hash = Settings.Instance.GetHashCode();
Settings.TextValue = "ButtonA OK";
}
}
}
MainWindow.xaml
<Window x:Class="bindings.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" WindowStyle="ToolWindow">
<Grid PresentationTraceSources.TraceLevel="High" DataContext="{StaticResource foobar}">
<Button Content="ButtonA" Height="33" HorizontalAlignment="Left" Margin="76,243,0,0" Name="button1" VerticalAlignment="Top" Width="101" Click="button1_Click" />
<TextBox Height="28" HorizontalAlignment="Left" Margin="182,180,0,0" Name="textBox1" VerticalAlignment="Top" Width="93"
Text="{Binding Path=TextValue, Mode=OneWay}" DataContext="{Binding}" PresentationTraceSources.TraceLevel="High"/>
</Grid>
</Window>
Thanks for help!
Scenario: In a Silverlight 4 MVVM project, we have a ListBox control containing items, the selected item is two-way-bound to the appropriate property in the ViewModel. Another control (for example reasons, I've stripped it down to a single TextBox) is data bound to the selected item's content. The value should update on leave/focus lost.
Problem: When the value in the TextBox is changed and we leave that TextBox by pressing the Tab key, everything works as desired - the value is updated. However, if the user clicks on a different item in the ListBox, then the SelectedItem setter is fired before the content of TextBox setter is fired, leaving no chance to handle the user input.
You can see in debugger, when adding breakpoints to the property setters, that the new ListView selection is applied first, before the TextBox update is processed.
Desired behavior: We need to know that the currently selected item was modified before the user has selected another item. It's not desired to have a custom update trigger which would notify on each key press (we know that's possible).
Can you help?
Code (a very simple example):
ViewModel
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ItemViewModel : ViewModelBase
{
private string _content;
public ItemViewModel(string initContent)
{
_content = initContent;
}
public string Content
{
get
{
return _content;
}
set
{
if (_content != value)
{
_content = value;
OnPropertyChanged("Content");
}
}
}
}
public class MainViewModel : ViewModelBase
{
private ObservableCollection<ItemViewModel> _items =
new ObservableCollection<ItemViewModel>();
private ItemViewModel _selectedViewModel;
public ObservableCollection<ItemViewModel> Items
{
get
{
return _items;
}
}
public ItemViewModel SelectedItem
{
get
{
return _selectedViewModel;
}
set
{
if (_selectedViewModel != value)
{
_selectedViewModel = value;
OnPropertyChanged("SelectedItem");
}
}
}
}
XAML
<Grid x:Name="LayoutRoot" Background="White">
<ListBox Height="100"
HorizontalAlignment="Left"
Margin="12,12,0,0"
VerticalAlignment="Top"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
DisplayMemberPath="Content"
Width="220" />
<TextBox Height="23"
HorizontalAlignment="Left"
Margin="12,118,0,0"
Text="{Binding SelectedItem.Content, Mode=TwoWay}"
VerticalAlignment="Top"
Width="220" />
</Grid>
XAML Code Behind
public MvvmTestView()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MvvmTestView_Loaded);
}
void MvvmTestView_Loaded(object sender, RoutedEventArgs e)
{
MainViewModel viewModel = new MainViewModel();
viewModel.Items.Add(new ItemViewModel("Hello StackOverflow"));
viewModel.Items.Add(new ItemViewModel("Thanks to Community"));
DataContext = viewModel;
}
UPDATE 1
I present a self designed solution for you to check out, which will be probably be the accepted one, I still want to encourage you to make comments and give your hints. Thanks.
You could add a behavior to your textbox to updated the binding every time the text is changed in the textbox. Maybe this solved your problems.
Here´s the code for the Behavior class:
public class UpdateTextBindingOnPropertyChanged : Behavior<TextBox> {
// Fields
private BindingExpression expression;
// Methods
protected override void OnAttached() {
base.OnAttached();
this.expression = base.AssociatedObject.GetBindingExpression(TextBox.TextProperty);
base.AssociatedObject.TextChanged+= OnTextChanged;
}
protected override void OnDetaching() {
base.OnDetaching();
base.AssociatedObject.TextChanged-= OnTextChanged;
this.expression = null;
}
private void OnTextChanged(object sender, EventArgs args) {
this.expression.UpdateSource();
}
}
Heres the XAML:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="Namespace of the class where UpdateTextBindingOnPropertyChanged is defined"
<TextBox Text="{Binding SelectedItem.Content, Mode=TwoWay}">
<i:Interaction.Behaviors>
<local:UpdateTextBindingOnPropertyChanged />
</i:Interaction.Behaviors>
</TextBox >
This is one solution we currently came up with. It has the advantage that it separates different tasks to the appropriate layer. For example, the View enforces an update of the binding, while the ViewModel tells the View to do so. Another advantage is that its handled synchronously, which would for example allow to check the content right before switching away, and the call-stack remains unchanged without raising "External Code" (Going over Dispatcher or even DispatcherTimer would do so) which is better for maintenance and flow control. A disadvantage is the new Event which has to be bound and handled (and finally unbound. I present an anonymous handler only for example reasons).
How to get there?
In ViewModelBase, implement a new ForceBindingUpdate event:
public abstract class ViewModelBase : INotifyPropertyChanged
{
// ----- leave everything from original code ------
public event EventHandler ForceBindingUpdate;
protected void OnForceBindingUpdate()
{
var handler = ForceBindingUpdate;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
In MainViewModel, update the setter of the SelectedItem property:
set // of SelectedItem Property
{
if (_selectedViewModel != value)
{
// Ensure Data Update - the new part
OnForceBindingUpdate();
// Old stuff
_selectedViewModel = value;
OnPropertyChanged("SelectedItem");
}
}
Update the MvvmTestView Code Behind to implement the new event:
void MvvmTestView_Loaded(object sender, RoutedEventArgs e)
{
// remains unchanged
Mvvm.MainViewModel viewModel = new Mvvm.MainViewModel();
viewModel.Items.Add(new Mvvm.ItemViewModel("Hello StackOverflow"));
viewModel.Items.Add(new Mvvm.ItemViewModel("Thanks to Community"));
// Ensure Data Update by rebinding the content property - the new part
viewModel.ForceBindingUpdate += (s, a) =>
{
var expr = ContentTextBox.GetBindingExpression(TextBox.TextProperty);
expr.UpdateSource();
};
// remains unchanged
DataContext = viewModel;
}
Last but not least, the minimal XAML Update: Give the TextBox a name by adding x:Name="ContentTextBox" Attribute to the TextBoxs XAML.
Done.
Actually, I don't know if this is the cleanest solution, but it gets close to what we had in mind.
Maybe you could handle TextBox LostFocus then (instead of listening to every key press)?
Other idea would be to keep a proxy property on the ViewModel instead of directly binding to SelectedItem.Content and writing some code to make sure the item is updated.
Solution №1
public class LazyTextBox: TextBox
{
//bind to that property instead..
public string LazyText
{
get { return (string)GetValue(LazyTextProperty); }
set { SetValue(LazyTextProperty, value); }
}
public static readonly DependencyProperty LazyTextProperty =
DependencyProperty.Register("LazyText", typeof(string), typeof(LazyTextBox),
new PropertyMetadata(null));
//call this method when it's really nessasary...
public void EnsureThatLazyTextEqualText()
{
if (this.Text != this.LazyText)
{
this.LazyText = this.Text;
}
}
}
Solution №2 (works as magic :) )
public class MainViewModel : ViewModelBase
{
private ObservableCollection<ItemViewModel> _items =
new ObservableCollection<ItemViewModel>();
private ItemViewModel _selectedViewModel;
public ObservableCollection<ItemViewModel> Items { get { return _items; } }
public ItemViewModel SelectedItem
{
get { return _selectedViewModel; }
set
{
if (_selectedViewModel != value)
{
if (SelectedItem != null)
{
SelectedItem.Content = SelectedItem.Content;
}
_selectedViewModel = value;
// A little delay make no harm :)
var t = new DispatcherTimer();
t.Interval = TimeSpan.FromSeconds(0.1);
t.Tick += new EventHandler(t_Tick);
t.Start();
}
}
}
void t_Tick(object sender, EventArgs e)
{
OnPropertyChanged("SelectedItem");
(sender as DispatcherTimer).Stop();
}
}
I know that in MVVM we do not want to put code in code behind. But in this instance it hurts nothing as it is entirely maintained in the UI and SOP is maintained.
By putting a ghost element to take focus we can swap the focus back in forth forcing
the text box to commit its contents. So in the code behind we take care of the focus wiggle.
But yet we still are using a relay command Update Command to execute the save. So the order is good as the Click event fires wiggling the view. And then the relay command UpdateCommand will fire and the textbox is committed and ready for update.
<MenuItem Header="_Save"
Command="{Binding UpdateCommand}" Click="MenuItem_Click">
</MenuItem>
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
UIElement elem = Keyboard.FocusedElement as UIElement;
Keyboard.Focus(ghost);
Keyboard.Focus(elem);
}
Solution #3
public abstract class ViewModelBase : INotifyPropertyChanged
{
private List<string> _propNameList = new List<string>();
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
_propNameList.Add(propertyName);
var t = new DispatcherTimer();
t.Interval = TimeSpan.FromSeconds(0);
t.Tick += new EventHandler(t_Tick);
t.Start();
}
void t_Tick(object sender, EventArgs e)
{
if (_propNameList.Count > 0)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(_propNameList[0]));
_propNameList.Remove(_propNameList[0]);
}
}
}
PS: it's the same timer.. but this solution is more generic..
I've been investigating why some of my controls aren't being garbage collected and noticed it's easy to prevent simple controls that inherit from ContentControl from ever being destroyed. Here's an example:
Here is my custom ContentControl:
public class MyCustomControl : ContentControl
{
public MyCustomControl()
{
Debug.WriteLine("Constructed");
}
~MyCustomControl()
{
Debug.WriteLine("Destroyed");
}
}
Now if I put it on a page like so:
<navigation:Page x:Class="SimpleTestBed.Views.CustomControl"
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"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:local="clr-namespace:SimpleTestBed"
d:DesignWidth="640" d:DesignHeight="480"
Title="CustomControl Page">
<Grid x:Name="LayoutRoot">
<StackPanel>
<local:MyCustomControl>
<TextBox Text="{Binding SomeProperty,Mode=TwoWay}"></TextBox>
</local:MyCustomControl>
</StackPanel>
</Grid>
With the following code behind:
public partial class CustomControl : Page
{
public CustomControl()
{
InitializeComponent();
this.DataContext = new CustomControlViewModel();
this.Unloaded += new RoutedEventHandler(OnUnloaded);
}
void OnUnloaded(object sender, RoutedEventArgs e)
{
this.DataContext = null;
}
// Executes when the user navigates to this page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
}
Then the view model is:
public class CustomControlViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
RaisePropertyChanged(propertyName);
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
private string _someProperty = "Initial Value";
public string SomeProperty
{
get { return _someProperty; }
set
{
if (_someProperty != value)
{
string oldValue = _someProperty;
_someProperty = value;
OnPropertyChanged("SomeProperty");
OnSomePropertyChanged(oldValue, value);
}
}
}
protected virtual void OnSomePropertyChanged(string oldValue, string newValue)
{
}
}
Now when I navigate away from this page and try garbage collecting with GC.Collect(), as long if I've made no changes to the text in the Textbox, the ContentControl and Page are destroyed as expected by the GC. But if I've edited some text and navigated away from the page and then tried to GC.Collect(), the ContentControl doesn't get garbage collected.
Can anyone explain this behavior?
Actually, you can force the GC to collect the control by 'flickering' the Template of the control when you unload:
void MyCustomControl_Unloaded(object sender, RoutedEventArgs e)
{
Debug.WriteLine("MyCustomControl Unloaded");
ControlTemplate oldTemplate = this.Template;
this.Template = null;
this.Template = oldTemplate;
}
I presume this destroys the current visual tree losing references of the tree's first component to its parent (the custom control). It certainly forces the control to recall OnApplyTemplate when the control is reloaded.
Is this the correct pattern for developing Silverlight controls without leaking? If so, it strikes me as a bit bizarre that the template isn't disposed automatically when the control unloads.
A good account of this behavior would be much appreciated as it goes right to the heart of the life-cycle of Silverlight controls.
My experience showed that memory leaks in Silverlight are coused by top 2 reason :
Events => Make sure that you remove attached events once when it is not needed or in class destructor.
Templates => Solution define templates in Resource section
I have a ComboBox that is bound to an ObservableCollection of custom UserControls. Each user control has a Tag value set and the ComboBox' DisplayMemberPath is set to "Tag". This correctly displays the Tag of each UserControl in the drop down list when the ComboBox is clicked, however when an item in the list is selected and the drop down list is closed, the ComboBox displays nothing in the button.
If I swap out a UserControl for a standard WPF control such as a TextBox, then it correctly displays the Tag value of the selected item, so it is something related to binding to a UserControl vs a standard WPF control. Also, if I set the IsEditable to True, then the editable TextBox displays the Tag correctly, but I don't want the text to be editable.
How do I get the Selected item to display when the ComboBox is not expanded?
Here is some sample code that replicates the issue:
(Note: The sample code is taken out of the context of the application it is running in so it looks a bit weird in what it is trying to do, but it still results in the same symptoms).
MyUC.xaml
<UserControl x:Class="ComboboxTest.MyUC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TextBox />
</Grid>
</UserControl>
Window1.xaml
<Window x:Class="ComboboxTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ComboboxTest"
Title="Window1" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<StackPanel Name="ControlsHolder">
<TextBox Tag="Box 1" Text="This is in Box 1" />
<TextBox Tag="Box 2" Text="This is in Box 2" />
<local:MyUC Tag="UC 1" />
<local:MyUC Tag="UC 2" />
</StackPanel>
<Grid>
<ComboBox Grid.Column="1"
Margin="5,0"
Name="MyComboBox"
ItemsSource="{Binding MyControls}"
DisplayMemberPath="Tag"
MinWidth="120"/>
</Grid>
</StackPanel>
Window1.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace ComboboxTest
{
public partial class Window1 : Window, INotifyPropertyChanged
{
ObservableCollection<MyUC> myControls = new ObservableCollection<MyUC>();
public Window1()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window1_Loaded);
}
void Window1_Loaded(object sender, RoutedEventArgs e)
{
myControls.Clear();
foreach (UIElement uiElement in this.ControlsHolder.Children)
{
MyUC tb = uiElement as MyUC;
if (tb != null)
{
myControls.Add(tb);
}
}
RaisePropertyChanged("MyControls");
}
public ObservableCollection<MyUC> MyControls
{
get
{
return this.myControls;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string p)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
}
}
This app appears as:
ComboBox with Drop Down Visible http://img229.imageshack.us/img229/5597/comboboxtestexpanded.png
And when "UC 2" is selected it appears as:
ComboBox with selected item not visible http://img692.imageshack.us/img692/4362/comboboxtestuc2selected.png
Binding a list of UIElements is not a good idea. Try using a wrapper class :
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window1_Loaded);
MyComboBox.ItemsSource = MyControls;
}
ObservableCollection<Wrapper> myControls = new ObservableCollection<Wrapper>();
void Window1_Loaded(object sender, RoutedEventArgs e)
{
myControls.Clear();
foreach (UIElement uiElement in this.ControlsHolder.Children)
{
MyUC tb = uiElement as MyUC;
if (tb != null)
{
myControls.Add(new Wrapper(tb));
}
}
RaisePropertyChanged("MyControls");
}
public ObservableCollection<Wrapper> MyControls
{
get
{
return this.myControls;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string p)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
}
public class Wrapper
{
public UserControl Control { get; protected set; }
public Wrapper(UserControl control)
{
Control = control;
}
public Object Tag
{
get { return Control.Tag; }
}
}