I'm trying to show selected items of treeview in textblock. This is my XAML code
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="vm:HLViewModel.SelectedNode" Value="{Binding ElementName="tree",Path=SelectedItem}"/>
</Trigger>
</Style.Triggers>
</Style>
Here is my textblock where I'm trying to show selected Item
<TextBlock Text="{Binding myText}"/>
I have created attached dependencyproperty which will be set when Treeview's IsSelected property is triggered. How Can I set the value of myText in the callback function?
public class HLViewModel : DependencyObject
{
public myText{get;set;}
public static object GetSelectedNode(DependencyObject obj)
{
return (object)obj.GetValue(SelectedNodeProperty);
}
public static void SetSelectedNode(DependencyObject obj, object value)
{
obj.SetValue(SelectedNodeProperty, value);
}
public static readonly DependencyProperty SelectedNodeProperty =
DependencyProperty.RegisterAttached("SelectedNode", typeof(object), typeof(HLViewModel), new PropertyMetadata("def",SelectedNode_changed));
private static void SelectedNode_changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// wanna set of myText property value here
}
Here's the simple way to have a viewmodel use the selected item from a TreeView:
XAML:
<TreeView
x:Name="MyTreeView"
SelectedItemChanged="MyTreeView_SelectedItemChanged"
Codebehind:
private void MyTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
(DataContext as MyViewModel).SelectedRoomLevelItem = e.NewValue;
}
Here's a much more sophisticated way of doing the same thing. This example demonstrates how attached properties are used in WPF. Note that setting TreeViewAttached.SelectedItem will not set the selected item of the tree view -- if you want to do that, it's doable, but troublesome. All this attached property does is allow you to write bindings which receive the selected item from the treeview when the selection changes.
public static class TreeViewAttached
{
#region TreeViewAttached.SelectedItem Attached Property
public static Object GetSelectedItem(TreeView obj)
{
return (Object)obj.GetValue(SelectedItemProperty);
}
public static void SetSelectedItem(TreeView obj, Object value)
{
obj.SetValue(SelectedItemProperty, value);
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.RegisterAttached("SelectedItem", typeof(Object), typeof(TreeViewAttached),
new FrameworkPropertyMetadata(null) {
BindsTwoWayByDefault = true
});
#endregion TreeViewAttached.SelectedItem Attached Property
#region TreeViewAttached.MonitorSelectedItem Attached Property
public static bool GetMonitorSelectedItem(TreeView obj)
{
return (bool)obj.GetValue(MonitorSelectedItemProperty);
}
public static void SetMonitorSelectedItem(TreeView obj, bool value)
{
obj.SetValue(MonitorSelectedItemProperty, value);
}
public static readonly DependencyProperty MonitorSelectedItemProperty =
DependencyProperty.RegisterAttached("MonitorSelectedItem", typeof(bool), typeof(TreeViewAttached),
new PropertyMetadata(false, MonitorSelectedItem_PropertyChanged));
private static void MonitorSelectedItem_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
(d as TreeView).SelectedItemChanged += TreeViewAttached_SelectedItemChanged;
}
else
{
(d as TreeView).SelectedItemChanged -= TreeViewAttached_SelectedItemChanged;
}
}
private static void TreeViewAttached_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
SetSelectedItem(sender as TreeView, e.NewValue);
}
#endregion TreeViewAttached.MonitorSelectedItem Attached Property
}
XAML:
<TreeView
local:TreeViewAttached.MonitorSelectedItem="True"
local:TreeViewAttached.SelectedItem="{Binding SelectedRoomLevelItem}"
ItemsSource="{Binding Items}"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<Label Content="{Binding HeaderText}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<Label Content="{Binding Path=SelectedRoomLevelItem.HeaderText}" />
My example uses a quickie treeview item datacontext class with Items and HeaderText properties. Your use of this code will have to adapt to the particular viewmodel classes in your project.
Related
I'm trying to synchronize selection in a DataGrid using a collection in my data. I have this mostly working, with one little quirk.
When I change selection in the DataGrid changes are written to my data collection, so far so good. Then, if the data collection changes selection in my DataGrid is updated, as expected. However, if I modify my data before modifying the DataGrid then the DataGrid selection does not update.
An example of the first, working case
An example of the second, non-working case
Code
using System.Collections;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace Testbed
{
public class Widget
{
public string Name { get; set; }
}
public class Data
{
public static Data Instance { get; } = new Data();
public ObservableCollection<Widget> Widgets { get; set; } = new ObservableCollection<Widget>();
public IList SelectedWidgets { get; set; } = new ObservableCollection<Widget>();
Data()
{
Widgets.Add(new Widget() { Name = "Widget 1" });
Widgets.Add(new Widget() { Name = "Widget 2" });
Widgets.Add(new Widget() { Name = "Widget 3" });
}
};
public class BindableDataGrid : DataGrid
{
public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(
"SelectedItems",
typeof(IList),
typeof(BindableDataGrid),
new PropertyMetadata(default(IList)));
public new IList SelectedItems
{
get { return (IList) GetValue(SelectedItemsProperty); }
set { SetValue(SelectedItemsProperty, value); }
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
SetCurrentValue(SelectedItemsProperty, base.SelectedItems);
}
}
public partial class MainWindow : Window
{
public MainWindow ()
{
InitializeComponent();
}
private void Button1_Click(object sender, RoutedEventArgs e) { Button_Clicked(0); }
private void Button2_Click(object sender, RoutedEventArgs e) { Button_Clicked(1); }
private void Button3_Click(object sender, RoutedEventArgs e) { Button_Clicked(2); }
private void Button_Clicked(int index)
{
Data data = Data.Instance;
Widget widget = data.Widgets[index];
if (data.SelectedWidgets.Contains(widget))
{
data.SelectedWidgets.Remove(widget);
}
else
{
data.SelectedWidgets.Add(widget);
}
}
}
}
And markup
<Window
x:Class="Testbed.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:test="clr-namespace:Testbed"
Title="MainWindow"
Height="480" Width="640"
DataContext="{Binding Source={x:Static test:Data.Instance}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="210" />
<ColumnDefinition Width="5" />
<ColumnDefinition MinWidth="210" />
<ColumnDefinition Width="5" />
<ColumnDefinition MinWidth="210" />
</Grid.ColumnDefinitions>
<!-- Change selection through data -->
<StackPanel Grid.Column="0">
<Button Content="Select Widget 1" Click="Button1_Click"/>
<Button Content="Select Widget 2" Click="Button2_Click"/>
<Button Content="Select Widget 3" Click="Button3_Click"/>
</StackPanel>
<!-- Current selection in data -->
<DataGrid Grid.Column="2"
ItemsSource="{Binding SelectedWidgets}"
IsReadOnly="true">
</DataGrid>
<!-- Change selection through UI -->
<test:BindableDataGrid Grid.Column="4"
SelectionMode="Extended"
ColumnWidth="*"
ItemsSource="{Binding Widgets}"
SelectedItems="{Binding SelectedWidgets, Mode=TwoWay}"
IsReadOnly="true">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="CornflowerBlue"/>
</Style.Resources>
</Style>
</DataGrid.RowStyle>
</test:BindableDataGrid>
</Grid>
</Window>
The problem occurs because you do not handle notifications of the BindableDataGrid.SelectedItems collection.
In the first case you do not need to handle them manually because you actually get the SelectedItems collection from the base DataGrid class and pass it to the view model from the OnSelectionChanged method call. The base DataGrid handle notifications of this collection itself.
However, if you click the button first, the SelectedItems property get a new collection and the base DataGrid knows nothing about it.
I think that you need to handle the propertyChangedCallback, and handle notifications of provided collections to update selection in the grid manually. Refer to the following code demonstrating the concept. Note that I have renamed the property for simplicity but still have not debugged it.
public static readonly DependencyProperty SelectedItemsNewProperty = DependencyProperty.Register(
"SelectedItemsNew",
typeof(IList),
typeof(BindableDataGrid), new PropertyMetadata(OnPropertyChanged));
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
BindableDataGrid bdg = (BindableDataGrid)d;
if (e.OldValue as INotifyCollectionChanged != null)
(e.NewValue as INotifyCollectionChanged).CollectionChanged -= bdg.BindableDataGrid_CollectionChanged;
if (Object.ReferenceEquals(e.NewValue, bdg.SelectedItems))
return;
if( e.NewValue as INotifyCollectionChanged != null )
(e.NewValue as INotifyCollectionChanged).CollectionChanged += bdg.BindableDataGrid_CollectionChanged;
bdg.SynchronizeSelection(e.NewValue as IList);
}
private void BindableDataGrid_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
SynchronizeSelection((IList)sender);
}
private void SynchronizeSelection( IList collection) {
SelectedItems.Clear();
if (collection != null)
foreach (var item in collection)
SelectedItems.Add(item);
}
This happens because your new SelectedItems property never updates the base SelectedItems when it is set. The problem with that is, of course, that MultiSelector.SelectedItems is readonly. It was designed specifically not to be set-able - but it was also designed to be updatable.
The reason your code works at all is because when you change the selection via the BindableDataGrid, SelectedWidgets gets replaced with the DataGrid's internal SelectedItemsCollection. After that point, you are adding and removing from that collection, so it updates the DataGrid.
Of course, this doesn't work if you haven't changed the selection yet, because OnSelectionChanged doesn't run until then, so SetCurrentValue is never called, so the binding never updated SelectedWidgets. But that's fine, all you have to do is called SetCurrentValue as part of BindableDataGrid's initialization.
Add this to BindableDataGrid:
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
SetCurrentValue(SelectedItemsProperty, base.SelectedItems);
}
Be careful, though, because this will still break if you try to set SelectedItems sometime after initialization. It would be nice if you could make it readonly, but that prevents it from being used in data binding. So make sure that your binding uses OneWayToSource not TwoWay:
<test:BindableDataGrid Grid.Column="4"
SelectionMode="Extended"
ColumnWidth="*"
ItemsSource="{Binding Widgets}"
SelectedItems="{Binding SelectedWidgets, Mode=OneWayToSource}"
IsReadOnly="true">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="CornflowerBlue"/>
</Style.Resources>
</Style>
</DataGrid.RowStyle>
</test:BindableDataGrid>
If you want to insure this never breaks, you can add a CoerceValueCallback to make sure the new SelectedItems is never set to something other than base.SelectedItems:
public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(
"SelectedItems",
typeof(IList),
typeof(BindableDataGrid),
new PropertyMetadata(default(IList), null, (o, v) => ((BindableDataGrid)o).CoerceBindableSelectedItems(v)));
protected object CoerceBindableSelectedItems(object baseValue)
{
return base.SelectedItems;
}
#Drreamer's answer pointed me in the right direction. However, it boiled down to embracing the fact that the source data collection was being replaced by the DataGrid.SelectedItems collection. It ends up bypassing OnPropertyChanged after the first modification because both ends of the binding are actually the same object.
I didn't want the source collection to be replaced so I found another solution that synchronizes the contents of the collections. It has the benefit of being more direct as well.
When SelectedItems is initialized by the DependencyProperty I stash a reference to the source and target collections. I also register for CollectionChanged on the source and override OnSelectionChanged on the target. Whenever one collection changes I clear the other collection and copy the contents over. As another bonus I no longer have to expose my source collection as IList to allow the DependencyProperty to work since I'm not using it after caching off the source.
public class BindableDataGrid : DataGrid
{
public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(
"SelectedItems",
typeof(IList),
typeof(BindableDataGrid),
new PropertyMetadata(OnPropertyChanged));
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
BindableDataGrid bdg = (BindableDataGrid) d;
if (bdg.initialized) return;
bdg.initialized = true;
bdg.source = (IList) e.NewValue;
bdg.target = ((DataGrid) bdg).SelectedItems;
((INotifyCollectionChanged) e.NewValue).CollectionChanged += bdg.OnCollectionChanged;
}
public new IList SelectedItems
{
get { return (IList) GetValue(SelectedItemsProperty); }
set { SetValue(SelectedItemsProperty, value); }
}
IList source;
IList target;
bool synchronizing;
bool initialized;
private void OnSourceChanged()
{
if (synchronizing) return;
synchronizing = true;
target.Clear();
foreach (var item in source)
target.Add(item);
synchronizing = false;
}
private void OnTargetChanged()
{
if (synchronizing) return;
synchronizing = true;
source.Clear();
foreach (var item in target)
source.Add(item);
synchronizing = false;
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
OnSourceChanged();
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
OnTargetChanged();
}
}
I'm sure there's is a much more elegant way to solve this, but this is the best I've got right now.
I have a DependencyObject that's inside an attached dependency property (that's a collection). Binding to that object does not work for some reason.
In my example, I am binding two things, a basic attached property (local:CollHolder.BasicProperty) and a regular dependent property (local:MyItem.MyData) - both are bound to the Text of a TextBox control.
The XAML looks like this:
<ListView ItemsSource="{x:Bind Items}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<StackPanel x:Name="stack" local:CollHolder.BasicProperty="{Binding ElementName=text, Path=Text}" VerticalAlignment="Center" HorizontalAlignment="Center" >
<TextBox Text="" x:Name="text"/>
<local:CollHolder.Coll>
<local:MyItem MyData="{Binding ElementName=text, Path=Text}"/>
</local:CollHolder.Coll>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
When changes happen the Text property, they propagate to the attached property, but not to the dependency property.
CollHolder:
public class CollHolder : DependencyObject
{
public static readonly DependencyProperty BasicPropertyProperty =
DependencyProperty.RegisterAttached("BasicProperty", typeof(string), typeof(CollHolder), new PropertyMetadata("", DPC));
public static readonly DependencyProperty CollProperty =
DependencyProperty.RegisterAttached("Coll", typeof(Coll), typeof(CollHolder), new PropertyMetadata(null));
public static Coll GetColl(DependencyObject obj)
{
var coll = (Coll)obj.GetValue(CollProperty);
if (coll == null)
{
obj.SetValue(CollProperty, coll = new Coll());
}
return coll;
}
public static void SetColl(DependencyObject obj, Coll value)
{
obj.SetValue(CollProperty, value);
}
public static string GetBasicProperty(DependencyObject obj)
{
return (string)obj.GetValue(BasicPropertyProperty);
}
public static void SetBasicProperty(DependencyObject obj, string value)
{
obj.SetValue(BasicPropertyProperty, value);
}
private static void DPC(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("Basic Property changed");
}
}
MyItem:
public class MyItem : DependencyObject
{
public string MyData
{
get { return (string)GetValue(MyDataProperty); }
set { SetValue(MyDataProperty, value); }
}
// Using a DependencyProperty as the backing store for MyData. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyDataProperty =
DependencyProperty.Register("MyData", typeof(string), typeof(MyItem), new PropertyMetadata("", DPC));
private static void DPC(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("CHANGED!!");
}
}
And the collection is pretty simple:
public class Coll : List<MyItem>
{
}
When changes happen the Text property, they propagate to the attached property, but not to the dependency property.
<TextBox Text="" x:Name="text"/>
<local:CollHolder.Coll>
<local:MyItem MyData="{Binding ElementName=text, Path=Text}"/>
</local:CollHolder.Coll>
The TextBox and local:MyItem in the same DataContext, you could use {x:Bind } to get the text value directly.
<TextBox Text="{x:Bind }" x:Name="text" />
<local:CollHolder.Coll>
<local:MyItem MyData="{x:Bind }" />
</local:CollHolder.Coll>
Update
I tried to replace MyItem with Control to test whether DependencyProperty works in Binding mode. It works as expected. So, you could use Control as MyItem's base class.
public class MyItem : Control
{
public string MyData
{
get { return (string)GetValue(MyDataProperty); }
set { SetValue(MyDataProperty, value); }
}
// Using a DependencyProperty as the backing store for MyData. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyDataProperty =
DependencyProperty.Register("MyData", typeof(string), typeof(MyItem), new PropertyMetadata("", DPC));
private static void DPC(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("CHANGED!!");
}
}
I am trying to use AttachedProperty in my AvalonDock, I want it to be part of LayoutAnchorable but PropertyChangedCallback never get called. i have binded AttachedPropert and i am getting the control over ViewModel ie: when binded property changes it trigger my ViewModel Property.
My AttachedProperty
public static readonly DependencyProperty IsCanVisibleProperty =
DependencyProperty.RegisterAttached("IsCanVisible", typeof(bool), typeof(AvalonDockBehaviour), new FrameworkPropertyMetadata(new PropertyChangedCallback(IsCanVisiblePropertyChanged)));
private static void IsCanVisiblePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LayoutAnchorable control = d as LayoutAnchorable;
if (control != null)
{
control.IsVisible = (bool)e.NewValue;
}
}
public static void SetIsCanVisible(DependencyObject element, bool value)
{
element.SetValue(IsCanVisibleProperty, value);
}
public static bool GetIsCanVisible(DependencyObject element)
{
return (bool)element.GetValue(IsCanVisibleProperty);
}
XAML
<xcad:DockingManager>
<xcad:LayoutRoot >
<xcad:LayoutPanel Orientation="Horizontal" >
<xcad:LayoutAnchorablePane >
<xcad:LayoutAnchorable Title="Folder" behv:AvalonDockBehaviour.IsCanVisible="{Binding IsHideExplorer, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<Views:ExplorerView DataContext="{Binding ExplorerViewModel}"/>
</xcad:LayoutAnchorable>
</xcad:LayoutAnchorablePane>
</xcad:LayoutPanel>
</xcad:LayoutRoot>
</xcad:DockingManager>
ViewModel Property
private bool _IsHideExplorer;
public bool IsHideExplorer
{
get { return _IsHideExplorer; }
set { _IsHideExplorer = value; NotifyPropertyChanged(); }
}
I have tried attaching the property to DockingManager the PropertyChangedCallback works. Any Help guys.
Did you already check the DataContext of your LayoutAnchorable? Maybe the DataContext is not passed down to it. In that case the Binding would not work and your DependencyProperty is not updated.
similar to my Labeled TextBox, which issues are resolved in:
Labeled TextBox in Windows Universal App
I got two issues in my Labeled Combobox, but first the Code:
Generic.xaml:
<Style TargetType="template:LabeledComboBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="template:LabeledComboBox">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="{TemplateBinding Label}" FontWeight="Bold" VerticalAlignment="Center" Margin="10,0" />
<ComboBox x:Name="PART_ComboBox" ItemsSource="{TemplateBinding ItemsSource}" SelectedIndex="{TemplateBinding SelectedIndex}" SelectedValue="{TemplateBinding SelectedValue}" SelectedValuePath="{TemplateBinding SelectedValuePath}" DisplayMemberPath="{TemplateBinding DisplayMemberPath}" VerticalAlignment="Center" Margin="20,0,10,0" Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
LabeledComboBox.cs:
[TemplatePart(Name = "PART_ComboBox", Type = typeof(ComboBox))]
public sealed class LabeledComboBox : Control, IParameterReturnable
{
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("Label", typeof(string), typeof(LabeledComboBox), new PropertyMetadata(""));
public string Label
{
get { return GetValue(LabelProperty).ToString(); }
set { SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(LabeledComboBox), new PropertyMetadata(null));
public object ItemsSource
{
get { return GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty SelectedIndexProperty = DependencyProperty.Register("SelectedIndex", typeof(int), typeof(LabeledComboBox), new PropertyMetadata(default(int)));
public int SelectedIndex
{
get { return (int) GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(object), typeof(LabeledComboBox), new PropertyMetadata(null));
public object SelectedValue
{
get { return GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
public static readonly DependencyProperty SelectedValuePathProperty = DependencyProperty.Register("SelectedValuePath", typeof(string), typeof(LabeledComboBox), new PropertyMetadata(default(string)));
public string SelectedValuePath
{
get { return GetValue(SelectedValuePathProperty).ToString(); }
set { SetValue(SelectedValuePathProperty, value); }
}
public static readonly DependencyProperty DisplayMemberPathProperty = DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(LabeledComboBox), new PropertyMetadata(default(string)));
public string DisplayMemberPath
{
get { return GetValue(DisplayMemberPathProperty).ToString(); }
set { SetValue(DisplayMemberPathProperty, value); }
}
private ComboBox _comboBox;
public LabeledComboBox()
{
this.DefaultStyleKey = typeof(LabeledComboBox);
}
public LabeledComboBox(List<Parameter> parameterList)
{
this.Label = parameterList[0].DisplayName ?? "";
this.ItemsSource = parameterList;
this.SelectedValuePath = "DefaultValue";
this.DisplayMemberPath = "DefaultValue";
this.SelectedIndex = 0;
this.DefaultStyleKey = typeof(LabeledComboBox);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_comboBox = GetTemplateChild("PART_ComboBox") as ComboBox;
if (_comboBox != null)
{
_comboBox.SelectionChanged += OnComboBoxSelectionChanged;
if (_comboBox.Items != null)
{
this.SelectedIndex = 0;
_comboBox.SelectedValue = _comboBox.Items[this.SelectedIndex];
}
}
}
private void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.SelectedValue = _comboBox.SelectedValue;
}
public string GetKey()
{
return Label;
}
public string GetValue()
{
return SelectedValue.ToString();
}
}
It will be called in two different ways:
Dynamically in C#:
stackPanel.Add(new LabeledComboBox(parameterList));
Static in Xaml:
<templates:LabeledComboBox Label="Kategorien:" ItemsSource="{Binding ElementName=pageRoot, Path=FeedCategories}" DisplayMemberPath="Name" SelectedValuePath="Name" />
As I said before I got two issues with it:
How can I bind the SelectionChangedEvent to access it in Xaml || C#
As you can see, I try to preselect the first Item, which does not work and I don't know how to do it right
Thank you very much for all helpful and well meant answers in advance!
Instead of creating a custom control and recreating all needed dependency properties, I would suggest you use the Header and HeaderTemplate properties of the built in ComboBox, which will be displayed, just like in your LabeledComboBox, above the selection menu. Additionally the SelectionChanged event will be available.
So the usage in XAML would look like the following:
<ComboBox
DisplayMemberPath="Name"
Header="Kategorien:"
ItemsSource="{Binding ElementName=pageRoot, Path=FeedCategories}"
SelectedValuePath="Name"
SelectionChanged="OnSelectionChanged">
<ComboBox.HeaderTemplate>
<DataTemplate>
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding}" />
</DataTemplate>
</ComboBox.HeaderTemplate>
</ComboBox>
But if you don't want to use the above method, to expose the selection changed event in your LabeledComboBox, add the following code:
private void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.SelectedValue = _comboBox.SelectedValue;
this.RaiseSelectionChanged(e);
}
public event EventHandler<SelectionChangedEventArgs> SelectionChanged;
private void RaiseSelectionChanged(SelectionChangedEventArgs args)
{
if (SelectionChanged != null)
{
SelectionChanged(this, args);
}
}
Then you can use the created SelectionChanged event from XAML.
I have a question concerning the LongListMultiSelector in the Windows Phone 8 Toolkit.
I want to use this control to implement a file browser in WP8 (using MVVM). Since the SelectedItems property is not bindable, I used the solution in this article to fix that.
http://dotnet-redzone.blogspot.de/2012/11/windows-phone-8longlistselector.html
Here's my relevant code:
XAML
<Grid DataContext="{Binding FileBrowserViewModel}">
<local:LongListMultiSelector
x:Name="FileList"
ItemsSource ="{Binding CurrentFileList}"
EnforceIsSelectionEnabled="{Binding IsInSelectionMode}"
toolkit:TiltEffect.IsTiltEnabled="True"
SelectedItems="{Binding SelectedFiles, Mode=TwoWay}"
IsSelectionEnabled="True"/>
</Grid>
My LonglistMultiSelector
public class LongListMultiSelector : Microsoft.Phone.Controls.LongListMultiSelector
{
public LongListMultiSelector()
{
SelectionChanged += LongListMultiSelector_SelectionChanged;
}
void LongListMultiSelector_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
this.SelectedItems = base.SelectedItems;
}
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register(
"SelectedItems",
typeof(object),
typeof(LongListMultiSelector),
new PropertyMetadata(null, OnSelectedItemsChanged)
);
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var selector = (LongListMultiSelector) d;
selector.SelectedItems = e.NewValue;
}
public new object SelectedItems
{
get { return GetValue(SelectedItemsProperty); }
set { SetValue(SelectedItemsProperty, value); }
}
}
VIEW MODEL
/// <summary>
/// The currently selected Items.
/// </summary>
public ObservableCollection<File> SelectedFiles
{
get { return _selectedFiles; }
set { Set(() => this.SelectedFiles, ref _selectedFiles, value); }
}
private ObservableCollection<File> _selectedFiles;
But this solution does not work. The SelectedFiles Property does not change at all. (_selectedFiles is always null)
Edit: Set(() => this.SelectedFiles, ref _selectedFiles, value); is from the Mvvmlight (Laurent Bugnion) package.
I solved my problem with using a normal LongListSelector and giving each Item inside it a Boolean IsSelected.
The DataTemplate then has a checkbox that looks like this:
<CheckBox IsChecked="{Binding IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}"/>