how can i get the selected item header in HandyControl hc:SideMenu WPF - wpf

i am using handy controls and this is first time to use it
so i am facing some difficulties
in test app i am using hc:SideMenu
and i can get out the selected item header from the hc:SideMenu
<hc:SideMenu
x:Name="MySideMenu"
Grid.Column="0"
Width="200"
Margin="5"
BorderThickness="1"
SelectionChanged="MySideMenu_SelectionChanged">
<hc:Interaction.Triggers>
<hc:EventTrigger EventName="SelectionChanged">
<hc:EventToCommand Command="{Binding SwitchItemCmd}" PassEventArgsToCommand="True" />
</hc:EventTrigger>
</hc:Interaction.Triggers>
<hc:SideMenuItem Header="Overview" IsSelected="True">
<hc:SideMenuItem.Icon>
<Image
Width="24"
Height="24"
Source="/Folder/01.png" />
</hc:SideMenuItem.Icon>
<hc:SideMenuItem
x:Name="MySummary"
Command="{Binding SelectCmd}"
CommandParameter="{Binding Header, RelativeSource={RelativeSource Self}}"
Header="Summary"
IsSelected="False">
<hc:SideMenuItem.Icon>
<TextBlock Text="" />
</hc:SideMenuItem.Icon>
</hc:SideMenuItem>
<hc:SideMenuItem
Command="{Binding SelectCmd}"
CommandParameter="{Binding Header, RelativeSource={RelativeSource Self}}"
Header="Dashboards">
<hc:SideMenuItem.Icon>
<TextBlock Text="" />
</hc:SideMenuItem.Icon>
</hc:SideMenuItem>
<hc:SideMenuItem
Command="{Binding SelectCmd}"
CommandParameter="{Binding Header, RelativeSource={RelativeSource Self}}"
Header="Wiki">
<hc:SideMenuItem.Icon>
<TextBlock Text="" />
</hc:SideMenuItem.Icon>
</hc:SideMenuItem>
</hc:SideMenuItem>
</hc:SideMenu>
and C# code is
private void MySideMenu_SelectionChanged(object sender, HandyControl.Data.FunctionEventArgs e)
{
string SelectedItemHeader = ((HeaderedSimpleItemsControl)MySideMenu.Header).ToString();
textBox.Text = SelectedItemHeader;
}

Try this:
private void MySideMenu_SelectionChanged(object sender,
HandyControl.Data.FunctionEventArgs<object> e)
{
SideMenuItem sideMenuItem = e.Info as SideMenuItem;
textBox.Text = sideMenuItem?.Header?.ToString();
}

Related

How to make a "SelectButton" with Button, Extender and ListBox to have width it needs?

I'm trying to take into use a SelectButton (https://gist.github.com/loraderon/580405) but I need to specify MinWidth for it. Otherwise it's width is just the width of Extender. Removing ColumnSpan or setting 1st column Auto are not doing the trick. I would really like it to always have width of most wide element in list + extender symbol.
<UserControl x:Class="loraderon.Controls.SelectButton"
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:my="clr-namespace:loraderon.Controls"
mc:Ignorable="d"
SizeChanged="UserControl_SizeChanged"
d:DesignHeight="30" d:DesignWidth="100">
<Grid
x:Name="SplitGrid"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="23" />
</Grid.ColumnDefinitions>
<Button
x:Name="Button"
Click="Button_Click"
Grid.ColumnSpan="2"
Padding="0"
HorizontalContentAlignment="Left"
>
<ContentControl
x:Name="ButtonContent"
HorizontalContentAlignment="Center"
ContentTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemTemplate}"
/>
</Button>
<Expander
x:Name="Expander"
Expanded="Expander_Expanded"
Collapsed="Expander_Collapsed"
Grid.Column="1"
VerticalAlignment="Center"
IsExpanded="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=IsExpanded}"
/>
<Popup
IsOpen="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=IsExpanded}"
PlacementTarget="{Binding ElementName=Button}"
PopupAnimation="Fade"
StaysOpen="False"
>
<ListBox
x:Name="ListBox"
SelectionMode="Single"
SelectionChanged="ListBox_SelectionChanged"
SelectedIndex="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=SelectedIndex, Mode=TwoWay}"
ItemTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemTemplate}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemsSource}"
/>
</Popup>
</Grid>
</UserControl
EDIT: The window I placed the control had:
SizeToContent="WidthAndHeight"
which resulted both answers below not to work. Is there more robust solution that would work when placing the button in variety of controls/containers? It seems that the way the control was built is not very robust. Popup not being the part of visual tree makes it a bad choice.
The easy part is binding to the ListBox' ActualWidth
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ElementName=ListBox, Path=ActualWidth}"/>
<ColumnDefinition Width="23" />
</Grid.ColumnDefinitions>
The tricky part is that since the ListBox is located in a Popup, with it's own visual tree (Remarks),
it only gets rendered when IsOpen is set to true.
The workaround is a swift open / close when the Control is loaded
public SelectButton()
{
InitializeComponent();
Loaded += (o, e) => Initialize();
}
void Initialize()
{
IsExpanded = true;
IsExpanded = false;
}
and an updated Expander_Expanded Method
private DateTime startUpTime = DateTime.Now;
private DateTime collapsedAt = DateTime.MinValue;
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
if (DateTime.Now - startUpTime <= TimeSpan.FromMilliseconds(200))
{
IsExpanded = true;
return;
}
if (DateTime.Now - collapsedAt <= TimeSpan.FromMilliseconds(200))
{
Expander.IsExpanded = false;
IsExpanded = false;
return;
}
IsExpanded = true;
}
EDIT
Turns out the TimeSpan of 200ms can be too small dependent on the system used, added a more robust solution
private bool startUp = true;
private DateTime collapsedAt = DateTime.MinValue;
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
if (startUp)
{
IsExpanded = true;
startUp = false;
return;
}
if (DateTime.Now - collapsedAt <= TimeSpan.FromMilliseconds(200))
{
Expander.IsExpanded = false;
IsExpanded = false;
return;
}
IsExpanded = true;
}
This is not pretty, but working. Since you already do Code-Behind, this might fit your needs:
First, the ItemsSourceProperty. Change it to:
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(SelectButton), new PropertyMetadata(ItemsSourceChanged ));
Second, prepare Constructor:
public SelectButton() {
InitializeComponent();
this.ListBox.Loaded += this.ListBoxOnLoaded;
}
Third, implement ItemnsSourceChanged-Method:
private static void ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var self = d as SelectButton;
self.ListBoxOnLoaded(self.ListBox, new RoutedEventArgs());
}
Fourth, do the magic:
private void ListBoxOnLoaded(object sender, RoutedEventArgs routedEventArgs) {
var lb = sender as ListBox;
lb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
this.col1.MinWidth = lb.DesiredSize.Width;
}
Last but not least, edit XAML:
<Grid x:Name="SplitGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" Name="col1" />
<ColumnDefinition Width="23" />
</Grid.ColumnDefinitions>
When the listbox has loaded, we simply do the measuring by ourself and apply the desired size to the first column.
Hope it helps :)
This is horrible answer but might give somebody an idea. I create an invisible Listbox to same location where the button content is and bind Grid.Column="0" MinWidth to it's ActualWidth.
Somehow this is a bit too wide. The width of the ListBox is too wide to assign to Grid.Column="0". The items in the popuplistbox are a lot more narrow. Max of these should be the width assigned to Grid.Column="0".
I also tried to have a buttton there and created additional dependencyproperty for its content. That was best looking (size was perfect) but then you would have to know preferably all the items and their sizes in different languages or at least one item. This is of course huge disadvantage.
EDIT: If this same could be achieved with ContentControl/ContentPresenter somehow to avoid 2 ListBox this would be far better.
EDIT2: This does not work. The Width is width of the 1st element so order or ItemsSource is relevant.
Here is the xaml:
<UserControl x:Class="loraderon.Controls.SelectButton"
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:my="clr-namespace:loraderon.Controls"
mc:Ignorable="d"
SizeChanged="UserControl_SizeChanged"
d:DesignHeight="30" d:DesignWidth="100">
<Grid
x:Name="SplitGrid"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="{Binding ActualWidth, ElementName=ContentListBox}"/>
<ColumnDefinition Width="23" />
</Grid.ColumnDefinitions>
<Button
x:Name="Button"
Click="Button_Click"
Grid.ColumnSpan="2"
Padding="0"
HorizontalContentAlignment="Left"
>
<ContentControl
x:Name="ButtonContent"
HorizontalContentAlignment="Center"
ContentTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemTemplate}"
/>
</Button>
<ListBox
Grid.Column="0"
x:Name="ContentListBox"
Visibility="Hidden"
MaxHeight="{Binding ActualHeight, ElementName=Button}"
HorizontalAlignment="Stretch"
ItemTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemTemplate}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemsSource}"/>
<Expander
x:Name="Expander"
Expanded="Expander_Expanded"
Collapsed="Expander_Collapsed"
Grid.Column="1"
VerticalAlignment="Center"
IsExpanded="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=IsExpanded}"
/>
<Popup
IsOpen="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=IsExpanded}"
PlacementTarget="{Binding ElementName=Button}"
PopupAnimation="Fade"
StaysOpen="False"
>
<ListBox
x:Name="ListBox"
SelectionMode="Single"
SelectionChanged="ListBox_SelectionChanged"
SelectedIndex="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=SelectedIndex, Mode=TwoWay}"
ItemTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemTemplate}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemsSource}"
/>
</Popup>
</Grid>
</UserControl
You can create a temporary ListBox and measure it to find the desired size for the element.
The most appropriate place to compute the size is when the ItemsSource property changes. You can achieve this by modifying the dependency property as such:
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(SelectButton), new PropertyMetadata(ItemSourceChanged));
In the ItemSourceChanged method you can create a temporary ListBox, make it have your items, and measure it:
private static void ItemSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ContentControl control = new ContentControl();
ListBox listBox = new ListBox();
control.Content = listBox;
IEnumerable enumerable = e.NewValue as IEnumerable;
SelectButton selectButton = d as SelectButton;
foreach (var item in enumerable)
{
listBox.Items.Add(item);
}
listBox.Measure(new Size(Double.MaxValue, Double.MaxValue));
selectButton.Button.Width = listBox.DesiredSize.Width;
}
Here the line control.Content = listBox; is necessary. If the ListBox is not contained within a control, desired size always returns 0.

How to set image path of specific ityem in ListView?

I have a ListView of Button elements like this:
<ListView ItemsSource="{Binding NumberOfItems}" SelectedItem="{Binding SelectedItem}">
<ListViewItem >
<Button Name="test" Grid.Row="0" Grid.Column="10" Grid.ColumnSpan="4" Grid.RowSpan="4" VerticalAlignment="Center" Background="Transparent" Command="{Binding DataContext.TestCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}">
<Button.Template>
<ControlTemplate>
<Grid RenderTransformOrigin="0.5,0.5" x:Name="bg">
<Image Source="{Binding DataContext.Test_ImagePath, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</ListViewItem >
</ListView>
My goal is to have button's image to toggle between two image paths on button click. It works, but the problem is that all the buttons in the list change the image path on some button click. I want only the one that is clicked to change the image path. I tried using CommandTarget property like this:
CommandTarget="{Binding DataContext.Listview.SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType=ListViewItem}}
but it didn't heplp.
Just to mention that I use MVVM.
How to solve this?
If you are using MVVM, I suppose you could wrap your models (As you said, integers for now) with a wrapper like this:
public class ToggleableWrapper<T> : INotifyPropertyChanged {
private bool toggled;
public ToggleableWrapper(T item){
this.Item = item;
this.ClickCommand = new RelayCommand(() => this.Toggled = !this.Toggled);
}
public T Item {get;}
public ICommand ClickCommand {get;}
public bool Toggled {
get { return this.toggled; }
set {
this.toggled = value;
OnPropertyChanged(nameof(this.Toggled));
}
}
//Property changed implementation...
}
So your NumberOfItems collection could look like this:
public ObservableCollection<ToggleableWrapper<int>> NumberOfItems {get;}
Now you need a ValueConverter which will convert the toggled boolean to your image. Call it ToggledToImageConverter
You can implement it accordingly and make it a resource somewhere.
Now your ListView looks like this:
<ListView ItemsSource="{Binding NumberOfItems}" SelectedItem="{Binding SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Name="test" Grid.Row="0" Grid.Column="10" Grid.ColumnSpan="4" Grid.RowSpan="4" VerticalAlignment="Center" Background="Transparent" Command="{Binding ClickCommand}">
<Button.Template>
<ControlTemplate>
<Grid RenderTransformOrigin="0.5,0.5" x:Name="bg">
<Image Source="{Binding Toggled, Converter={StaticResouce ToggledToImageConverter}"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
<DataTemplate>
</ListView.ItemTemplate>
</ListView>
So when you click the button, the bool is toggled, which will then toggle the image using the ValueConverter.

TreeViewItem MouseDoubleClick event and MvvmLight

How do I set CommandParameter in the below code so that it will point to currently selected item?
<TreeView Grid.Column="0" HorizontalAlignment="Stretch" DockPanel.Dock="Left" ItemsSource="{Binding Path=ServerItems, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<cmd:EventToCommand Command="{Binding ConnectServer}" PassEventArgsToCommand="True" CommandParameter="{Binding SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Databases}">
<TextBlock Text="{Binding}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
ViewModel code:
public RelayCommand<ServerItem> ConnectServer {
get;
private set;
}
ConnectServer = new RelayCommand<ServerItem>(param => ConnectToServer(param));
public void ConnectToServer(ServerItem item) {
MessageBox.Show(item.ToString());
}
Code execution doesn't get to ConnectToServer method because exception is thrown, telling me that cast from System.Windows.Input.MouseButtonEventArgs to type MadMin.Model.ServerItem is not possible.
You'll need to use a RelativeSource Binding in order to reach the TreeView.SelectedItem property from within the Trigger. Try this Binding for your CommandParameter instead:
CommandParameter="{Binding SelectedItem,
RelativeSource={RelativeSource AncestorType={x:Type TreeView}}}"
{Binding SelectedItem, RelativeSource={RelativeSource Self}}

Set Grid background color programmatically in Silverlight

I have a Data template
<DataTemplate x:Key="ConnectorItemFactory">
<Button Style="{StaticResource TextOnlyActionTileButtonStyle}" Margin="0,0,5,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Content="{Binding}"
ContentTemplate="{StaticResource TileTemplate}"
Command="{StaticResource NavigationCommand}"
CommandParameter="{Binding}"
Height="156"
Width="156"
>
<i:Interaction.Behaviors>
<UICommon:XboxBehavior VuiBinding="{Binding VuiTitle}"/>
</i:Interaction.Behaviors>
</Button>
</DataTemplate>
as a ContentTemplate it is used another data template
<DataTemplate x:Key="TileTemplate">
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="{StaticResource ActionButtonBackgroundBrush}">
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="7,7,7,4">
<Controls:TrimmedTextBlock Text="{Binding Title}" Style="{StaticResource TextOnlyTileTitleStyle}" TextWrapping="Wrap" />
<Controls:TrimmedTextBlock Text="{Binding Converter={StaticResource SubtitleTextFormatter}}" Style="{StaticResource TextOnlyTileSubtitleStyle}"/>
</StackPanel>
</Grid>
</DataTemplate>
I'm using a TemplateFactory to load the content template
public class GridTemplateFactory : ModelViewTemplateFactory
{
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
var dataTemplate = Application.Current.Resources["ConnectorItemFactory"] as DataTemplate;
// var grid = ((Button)dataTemplate.LoadContent()).ContentTemplate.LoadContent();
// ((Grid)grid).Background = new SolidColorBrush(Colors.Orange);
// ((Button)dataTemplate.LoadContent()).ContentTemplate.LoadContent().SetValue(Grid.BackgroundProperty, new SolidColorBrush(Colors.Orange));
this.ContentTemplate = dataTemplate;
}
}
In the OnContentChanged method I want to change the Grid background color from the TileTemplate data template.
I tried to do it like in commented code above, but it doesn't work. How can I change the color here?

TabControl SelectedItem Binding Problems

In a Tabbed interface application i'm trying to pass the TabItem Header string or the object contained in the selected TabItem to the view model to use it to
publish an event like this:
In the View (xaml):
<TabControl x:Name="MyTC"
prism:RegionManager.RegionName="{x:Static inf:RegionNames.MainRegion}"
SelectedItem="{Binding Path=TabControlSelectedItem,UpdateSourceTrigger=PropertyChanged,Mode=Twoway}"
Cursor="Hand"
Grid.Row="0"
Grid.Column="1">
<TabControl.ItemTemplate>
<DataTemplate>
<!--DataContext="{Binding ElementName=MyTC, Path=SelectedItem}"-->
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Margin="3"
Text="{Binding Path=DataContext.DataContext.HeaderInfo, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabItem}}}"
/>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding HeaderClickCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
In View Model
//***********************************************************
//Constructor
public ShellWindowViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
this.HeaderClickCommand = new DelegateCommand(OnHeaderClick);
}
//SelectedItem Binding
private object tabControlSelectedItem;
public object TabControlSelectedItem
{
get { return tabControlSelectedItem; }
set
{
if (tabControlSelectedItem != value)
{
tabControlSelectedItem = value;
OnPropertyChanged("TabControlSelectedItem");
}
}
}
//*****************************************************************************************************
//this handler publish the Payload "SelectedSubsystem" for whoever subscribe to this event
private void OnHeaderClick()
{
//EA for communication between Modules not within Modules
string TabHeader = (TabControlSelectedItem as TabItem).Header.ToString();
eventAggregator.GetEvent<SubsystemIDSelectedEvent>().Publish(TabHeader);
}
but there is something wrong because when i click the TabItem nothing happen and when i inserted a breakpoint # TabControlSelectedItem
property i found it contain the view namespace.i want the TabControlSelectedItem to get the selected Tab header string or the object in
the selected tab item.
Your help is very much appreciated.
I just tried something similar and it works fine.
Here is my xaml
<TabControl ItemsSource="{Binding Matches}"
SelectedItem="{Binding SelectedRecipe}">
<TabControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding Path=Date}" />
<TextBlock Grid.Column="1"
TextAlignment="Center"
Text="{Binding Path=Name}" />
</Grid>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
And then in my viewmodel
public object SelectedRecipe
{
get
{
return _selectedRecipe;
}
set
{
_selectedRecipe = value;
OnNotifyPropertyChanged("SelectedRecipe");
}
}

Resources