Toolbar item image change when pressed - wpf

I have a toolbar that binds it's ItemsSource to my VM, it's a list of ToolBarItem
public class ToolbarItem : ObservableObject
{
public string ToolTip { get; set; }
public ICommand Command { get; set; }
public BitmapImage Icon { get; set; }
private bool _isPressed;
public bool IsPressed
{
get { return _isPressed; }
set { _isPressed = value; RaisePropertyChanged("IsPressed"); }
}
public ToolbarItem(string tooltip, ICommand command, BitmapImage icon)
{
this.Icon = icon;
this.Command = command;
this.ToolTip = tooltip;
}
}
}
this is my Toolbar item template:
<DataTemplate x:Key="toolBarItemTemplate">
<Button x:Name="toolBarButton"
ToolTip="{Binding Path=ToolTip}"
Command="{Binding Path=Command}"
Cursor="Hand"
Style="{StaticResource toolBarButtonItemStyle}">
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource toolBarButtonItemTemplate}" />
</Button>
</DataTemplate>
<DataTemplate x:Key="toolBarButtonItemTemplate">
<Image Width="16"
Height="16"
Source="{Binding Path=Icon}"
Style="{StaticResource toolBarButtonImageStyle}" />
</DataTemplate>
What I want to do is, when the user clicks on specific toolbar button to change that button behavior, for example, have a border around it.

Assuming you don't want to use it as a ToggleButton, try something like this:
<DataTemplate x:Key="toolBarItemTemplate">
<Button x:Name="toolBarButton"
ToolTip="{Binding Path=ToolTip}"
Command="{Binding Path=Command}"
Cursor="Hand"
Style="{StaticResource toolBarButtonItemStyle}">
<!-- added code starts-->
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="BorderBrush" Value="#FF0000"/>
<Setter Property="Foreground" Value="#00FF00"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
<!-- added code ends-->
<ContentControl Content="{Binding}"
ContentTemplate="{StaticResource toolBarButtonItemTemplate}" />
</Button>
</DataTemplate>
<DataTemplate x:Key="toolBarButtonItemTemplate">
<Image Width="16"
Height="16"
Source="{Binding Path=Icon}"
Style="{StaticResource toolBarButtonImageStyle}" />
</DataTemplate>

the way to do it, is using ItemTemplateSelector and based on the item's type, use a different DataTemplate.
this is done in code

Related

How to properly use radiobutton checked event in mvvm pattern?

When a particular radiobutton is checked/clicked on I want to write some specific text for each of those radiobuttons in a particular textblock. How do I do that following the MVVM pattern ?
In my main viewmodel I have the following code...
public ICommand MyCommand { get; set; }
private string _txt;
public string Txt
{
get
{
return _txt;
}
set
{
_txt = value;
OnPropertyChanged("Txt");
}
}
private bool canexecutemethod(object parameter)
{
if (parameter != null)
{
return true;
}
else
{
return false;
}
}
private void executemethod(object parameter)
{
//what to do here;
}
public MainViewModel()
{
MyCommand = new RelayCommand(executemethod);
}
Also in the xaml:
<RadioButton VerticalAlignment="Center" Margin="15" Content="Name" Command="{Binding MyCommand}"/>
<RadioButton VerticalAlignment="Center" Margin="15" Content="Age" Command="{Binding MyCommand}"/>
<RadioButton VerticalAlignment="Center" Margin="15" Content="DOB" Command="{Binding MyCommand}"/>
<TextBlock Margin="5" Height="30" Width="150" Foreground="Red" Text="{Binding Txt, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
Say when the first radiobutton is checked the textblock text should show some text like
Please enter a name
, when the second radiobutton is checked the textblock text should show some text like
Please enter a valid age/integer value
and so on...
it can be done without view model participation:
<RadioButton VerticalAlignment="Center" Margin="15" Content="Name" Name="chkName"/>
<RadioButton VerticalAlignment="Center" Margin="15" Content="Age" Name="chkAge"/>
<RadioButton VerticalAlignment="Center" Margin="15" Content="DOB" Name="chkDob"/>
<TextBlock Margin="5" Height="30" Width="150" Foreground="Red">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=chkName,Path=IsChecked}" Value="True">
<Setter Property="Text" Value="Please enter a name"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=chkAge,Path=IsChecked}" Value="True">
<Setter Property="Text" Value="Please enter a valid age/integer value"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>

XAML to add header to radio button

So with a lot of looking around I am hoping to make a GroupBox that acts like a Radio button. The header section would act as the bullet. I took some code from this question
Styling a GroupBox
that is how I want it to look. But I want to have it as a Radio button. So I put in this code (mind you I've only been doing WPF for a week or 2 now)
<Style TargetType="{x:Type RadioButton}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<BulletDecorator>
<BulletDecorator.Bullet>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border x:Name="SelectedBorder"
Grid.Row="0"
Margin="4"
BorderBrush="Black"
BorderThickness="1"
Background="#25A0DA">
<Label x:Name="SelectedLabel" Foreground="Wheat">
<ContentPresenter Margin="4" />
</Label>
</Border>
<Border>
</Border>
</Grid>
</BulletDecorator.Bullet>
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="SelectedBorder" Property="Background" Value="PaleGreen"/>
<Setter TargetName="SelectedLabel"
Property="Foreground"
Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have a feeling that I can add a label to the second row of my grid, but then I don't know how to access it. I have that template in a test project in the Window.Resources section (I plan on moving it to a resource dictionary in my main project)
the xaml for my window is this
<Grid>
<GroupBox Name="grpDoor" Margin ="8" Grid.Row="0" Grid.Column="0">
<GroupBox.Header>
WPF RadioButton Template
</GroupBox.Header>
<StackPanel Margin ="8">
<RadioButton FontSize="15" Content="Dhaka" Margin="4" IsChecked="False"/>
<RadioButton FontSize="15" Content="Munshiganj" Margin="4" IsChecked="True" />
<RadioButton FontSize="15" Content="Gazipur" Margin="4" IsChecked="False" />
</StackPanel>
</GroupBox>
</Grid>
I then hoping for something like this (not sure how I'd do it yet though)
<Grid>
<GroupBox Name="grpDoor" Margin ="8" Grid.Row="0" Grid.Column="0">
<GroupBox.Header>
WPF RadioButton Template
</GroupBox.Header>
<StackPanel Margin ="8">
<RadioButton FontSize="15"
Content="Dhaka"
Margin="4"
IsChecked="False">
<RadioButton.Description>
This is a description that would show under my Header
</RadioButton.Description>
</RadioButton>
<RadioButton FontSize="15"
Content="Munshiganj"
Margin="4"
IsChecked="True">
<RadioButton.Description>
This is a description that would show under my Header
</RadioButton.Description>
</RadioButton>
<RadioButton FontSize="15"
Content="Gazipur"
Margin="4"
IsChecked="False">
<RadioButton.Description>
This is a description that would show under my Header
</RadioButton.Description>
</RadioButton>
</StackPanel>
</GroupBox>
</Grid>
Based on your clarification, here is a very simple example with a RadioButton that looks like a GroupBox.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:SimpleViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type local:SimpleOption}">
<RadioButton GroupName="choice" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}">
<RadioButton.Template>
<ControlTemplate TargetType="{x:Type RadioButton}">
<GroupBox x:Name="OptionBox" Header="{Binding Path=DisplayName, Mode=OneWay}">
<TextBlock Text="{Binding Path=Description, Mode=OneWay}"/>
</GroupBox>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, Mode=OneWay}" Value="True">
<Setter TargetName="OptionBox" Property="Background" Value="Blue"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</RadioButton.Template>
</RadioButton>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Path=Options, Mode=OneWay}"/>
</Grid>
</Window>
public class SimpleViewModel
{
public SimpleViewModel()
{
Options = new ObservableCollection<SimpleOption>();
var _with1 = Options;
_with1.Add(new SimpleOption {
DisplayName = "Dhaka",
Description = "This is a description for Dhaka."
});
_with1.Add(new SimpleOption {
DisplayName = "Munshiganj",
Description = "This is a description for Munshiganj.",
IsSelected = true
});
_with1.Add(new SimpleOption {
DisplayName = "Gazipur",
Description = "This is a description for Gazipur."
});
}
public ObservableCollection<SimpleOption> Options { get; set; }
}
public class SimpleOption : INotifyPropertyChanged
{
public string DisplayName {
get { return _displayName; }
set {
_displayName = value;
NotifyPropertyChanged("DisplayName");
}
}
private string _displayName;
public string Description {
get { return _description; }
set {
_description = value;
NotifyPropertyChanged("Description");
}
}
private string _description;
public bool IsSelected {
get { return _isSelected; }
set {
_isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
private bool _isSelected;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
}
I'd do it with a custom attached property. That way, you can bind to it from a ViewModel, or apply it directly in XAML.
First, create a new class in your Style assembly:
public static class RadioButtonExtender
{
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.RegisterAttached(
"Description",
typeof(string),
typeof(RadioButtonExtender),
new FrameworkPropertyMetadata(null));
[AttachedPropertyBrowsableForType(typeof(RadioButton))]
public static string GetDescription(RadioButton obj)
{
return (string)obj.GetValue(DescriptionProperty);
}
public static void SetDescription(RadioButton obj, string value)
{
obj.SetValue(DescriptionProperty, value);
}
}
And your style's Bullet would change so that the label is:
<Label x:Name="SelectedLabel"
Foreground="Wheat"
Content="{Binding (prop:RadioButtonExtender.Description), RelativeSource={RelativeSource TemplatedParent}} />
You could then use it in your final XAML:
<RadioButton FontSize="15"
Content="Dhaka"
Margin="4"
IsChecked="False">
<prop:RadioButtonExtender.Description>
This is a description that would show under my Header
</prop:RadioButtonExtender.Description>
</RadioButton>
As an added bonus, since you're creating the Style in a separate assembly, you can create a custom XAML namespace to make using your property easier.

How to convert a manually created WPF Menuitem into a Template / Style Resource / Control Template

I have manualy created a MenuItem. Now I want it as a Template / Style Resource / Control Template - whatever the best is for this Task.
My MenuItem looks like this (I know short Code):
<MenuItem
x:Name="Quit" << OUTSIDE TEMPLATE
Command="{Binding ShutdownCommand}"> << OUTSIDE TEMPLATE
<MenuItem.Header>
<StackPanel
Orientation="Horizontal">
<TextBlock
Width="150"
Text="Quit ERD Builder"/> << OUTSIDE TEMPLATE
<TextBlock
Width="80"
Margin="0,2,0,0"
TextAlignment="Right">
<Border
Padding="4,0,4,0"
BorderBrush="#B0B0B0"
Background="#fff"
BorderThickness="1"
CornerRadius="6">
<TextBlock
Width="Auto"
Text="Alt+F4" << OUTSIDE TEMPLATE
FontSize="10"
Foreground="#555" />
</Border>
</TextBlock>
</StackPanel>
</MenuItem.Header>
<MenuItem.Icon>
<Image
Width="16"
Height="16"
Margin="0,0,5,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality"
SnapsToDevicePixels="True">
<Image.Source>
<BitmapImage
UriSource="/ERDBuilder;component/icons/bw/102-walk.png" /> << OUTSIDE TEMPLATE
</Image.Source>
</Image>
</MenuItem.Icon>
The Lines I declared with << OUTSIDE TEMPLATE are the Lines I want declare in the MenuItem and not in the Template.
I had have already tried some Styles but "Background" for e.g. wont work for some Reason. I am able to change the "FontSize" but not the "Background" Color:
<Style
x:Key="TopTaskBarMenuitem"
TargetType="MenuItem">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#ffff00" /> << DONT WORK
<Setter Property="FontSize" Value="20" /> << WORKS
</Trigger>
</Style.Triggers>
<Setter Property="Foreground" Value="#000" /> << WORKS
<Setter Property="BorderThickness" Value="1" /> << WORKS
<Setter Property="Width" Value="150"/> << WORKS
This is the Menu how it looks if I `XAML' it manually:
Manualy created Menuitem (I am not allowed to upload Images here ?!)
And this is the Menuitem with the static Style Resource:
Menuitem with Style Resource
As you can see, the "Background" Color wont affect the Menuitem.
If I could wish me something I would have in the End something like this on the "Menuitem"-Side:
<MenuItem
Style="{StaticResource TopTaskBarMenuitem}" << TEMPLATE / STYLE BINDING
x:Name="Quit" << OUTSIDE TEMPLATE
Command="{Binding ShutdownCommand}" << OUTSIDE TEMPLATE
MyHeaderText="Quit ERD Builder"/> << OUTSIDE TEMPLATE
MyShortcutText="Alt+F4" << OUTSIDE TEMPLATE
MyUriSource="/ERDBuilder;component/icons/bw/102-walk.png" /> << OUTSIDE TEMPLATE
Thanks a lot to all they will Help!
PS: The last Codeline are missing on all three Code-Postings here. I dont know why. I am not able to fix this.
Dirk
To acquire this you have to create your own control deriving from MenuItem.
All you need to do is create the control class using DependencyProperties to make use of all its benefits, read this for more:
namespace MyControls
{
class MyMenuItem : MenuItem
{
public string MyHeaderText
{
get { return (string)GetValue(MyHeaderTextProperty); }
set { SetValue(MyHeaderTextProperty, value); }
}
public static readonly DependencyProperty MyHeaderTextProperty = DependencyProperty.Register("MyHeaderText", typeof(string), typeof(MyMenuItem));
public string MyShortcutText
{
get { return (string)GetValue(MyShortcutTextProperty); }
set { SetValue(MyShortcutTextProperty, value); }
}
public static readonly DependencyProperty MyShortcutTextProperty = DependencyProperty.Register("MyShortcutText", typeof(string), typeof(MyMenuItem));
public string MyUriSource
{
get { return (string)GetValue(MyUriSourceProperty); }
set { SetValue(MyUriSourceProperty, value); }
}
public static readonly DependencyProperty MyUriSourceProperty = DependencyProperty.Register("MyUriSource", typeof(string), typeof(MyMenuItem));
}
}
Now you are able to instantiate your control, but you still need to "retemplate" it:
<mc:MyMenuItem MyHeaderText="Quit ERD Builder" MyShortcutText="Alt+F4" MyUriSource="/ERDBuilder;component/icons/bw/102-walk.png">
<mc:MyMenuItem.Style>
<Style TargetType="mc:MyMenuItem">
<Style.Setters>
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Width="150" Text="{Binding Mode=TwoWay, Path=MyHeaderText, RelativeSource={RelativeSource FindAncestor, AncestorType=mc:MyMenuItem}}"/>
<TextBlock Width="80" Margin="0,2,0,0" TextAlignment="Right">
<Border Padding="4,0,4,0" BorderBrush="#B0B0B0" Background="#fff" BorderThickness="1" CornerRadius="6">
<TextBlock Width="Auto" Text="{Binding Mode=TwoWay, Path=MyShortcutText, RelativeSource={RelativeSource FindAncestor, AncestorType=mc:MyMenuItem}}" FontSize="10" Foreground="#555" />
</Border>
</TextBlock>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Icon">
<Setter.Value>
<Image Width="16" Height="16" Margin="0,0,5,0" HorizontalAlignment="Center" VerticalAlignment="Center" RenderOptions.BitmapScalingMode="HighQuality" SnapsToDevicePixels="True" Source="{Binding Mode=OneWay, Path=MyUriSource, RelativeSource={RelativeSource FindAncestor, AncestorType=mc:MyMenuItem}}" />
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</mc:MyMenuItem.Style>
</mc:MyMenuItem>
Don't forget to reference the namespace of this new control in your window (or wherever you may put this control) tag:
xmlns:mc="clr-namespace:MyControls"
It's possible to insert this style in a ResourceDictionary so you don't need to reference it everytime you use this control.
<Style TargetType="mc:MyMenuItem">
<!-- Style comes here -->
</Style>
Then you can acquire what you asked:
<mc:MyMenuItem MyHeaderText="Quit ERD Builder" MyShortcutText="Alt+F4" MyUriSource="/ERDBuilder;component/icons/bw/102-walk.png" />
I wish it can help you!
CoreStyle.xaml
The part from the Template / Style:
<Setter Property="Icon">
<Setter.Value>
<ctrl:Bitmap>
<ctrl:Bitmap.Source>
<!-- This doesnt work: --> <BitmapImage UriSource="{Binding Mode=OneWay, Path=MenuIcon, RelativeSource={RelativeSource FindAncestor, AncestorType=ctrl:MainMenuItem}}" />
<!-- This Still works fine: <BitmapImage UriSource="../Resources/Icons/16/page_add.png" />-->
</ctrl:Bitmap.Source>
</ctrl:Bitmap>
</Setter.Value>
</Setter>
MainMenuItem.cs
The Custom Control class that derives from MenuItem:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace ErdBuilder.Shell.Controls
{
class MainMenuItem : MenuItem
{
public ICommand MenuCommand
{
get { return (ICommand) GetValue(MenuCommandProperty); }
set { SetValue(MenuCommandProperty, value); }
}
public static readonly DependencyProperty MenuCommandProperty = DependencyProperty.Register("MenuCommand", typeof(ICommand), typeof(MainMenuItem));
public string MenuText
{
get { return (string)GetValue(MenuTextProperty); }
set { SetValue(MenuTextProperty, value); }
}
public static readonly DependencyProperty MenuTextProperty = DependencyProperty.Register("MenuText", typeof(string), typeof(MainMenuItem));
public string MenuShortcut
{
get { return (string)GetValue(MenuShortcutProperty); }
set { SetValue(MenuShortcutProperty, value); }
}
public static readonly DependencyProperty MenuShortcutProperty = DependencyProperty.Register("MenuShortcut", typeof(string), typeof(MainMenuItem));
public string MenuIcon
{
get { return (string)GetValue(MenuIconProperty); }
set { SetValue(MenuIconProperty, value); }
}
public static readonly DependencyProperty MenuIconProperty = DependencyProperty.Register("MenuIcon", typeof(string), typeof(MainMenuItem));
}
}
I also tried this:
public BitmapImage MenuIcon
{
get { return new BitmapImage(new Uri((string)GetValue(MenuIconProperty))); }
set { SetValue(MenuIconProperty, value); }
}
public static readonly DependencyProperty MenuIconProperty = DependencyProperty.Register("MenuIcon", typeof(BitmapImage), typeof(MainMenuItem));
Shell.xaml
And finaly the Part where I try to use the new Control:
<ctrl:MainMenuItem x:Name="TestMenu"
MenuCommand="{x:Static ApplicationCommands.New}"
MenuText="New..."
MenuShortcut="Ctr+N"
MenuIcon="../Resources/Icons/16/page_add.png"/>

Windows Explorer Tooltip in WPF

its not that hard what i want, but i'm pulling my hairs for days!
i just want the same tooltip behaviour like the WIndows Explorer:
overlay a partially hidden tree/list element with the tooltip that displays the full element
i use the following datatemplate in my treeview
<HierarchicalDataTemplate DataType="{x:Type TreeVM:SurveyorTreeViewItemViewModel}" ItemsSource="{Binding Children, Converter={StaticResource surveyorSortableCollectionViewConverter}}">
<StackPanel x:Name="SurveyorStackPanel" Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Horizontal" Height="20" Width="auto">
... (Textblocks, properties, usercontrol, border,... )
<StackPanel.ToolTip>
<ToolTip Placement="RelativePoint" Padding="0" HasDropShadow="False"
DataContext="{Binding ElementName=SurveyorStackPanel}">
<Rectangle HorizontalAlignment="Left" VerticalAlignment="Center"
Width="{Binding ElementName=SurveyorStackPanel, Path=Width}"
Height="{Binding ElementName=SurveyorStackPanel, Path=Height}">
<Rectangle.Fill>
<VisualBrush AutoLayoutContent="True" AlignmentX="Left"
Visual="{Binding}" Stretch="None"/>
</Rectangle.Fill>
</Rectangle>
</ToolTip>
</StackPanel.ToolTip>
</StackPanel>
</HierarchicalDataTemplate>
As you can see, i'm trying to use Visualbrush. but this doesnt work. it only shows what you see on the screen.
I have tried with static resource and binding on a new stackpanel thats in the tooltip, but that only leaves with a blanc tooltip.
Do i something wrong? do i have to use alternatives?
i'm pretty new in WPF. i know the basics, but binding/resources is kinda new for me
EDIT
here is the static source i tried:
<ToolTip x:Key="reflectingTooltip" DataContext="{Binding Path=PlacementTarget, RelativeSource={x:Static RelativeSource.Self}}" Placement="RelativePoint" Padding="0" HasDropShadow="False">
<Rectangle Width="{Binding ActualWidth}" Height="{Binding Path=ActualHeight}" Margin="0"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Rectangle.Fill>
<VisualBrush Visual="{Binding}" Stretch="None" AlignmentX="Left" />
</Rectangle.Fill>
</Rectangle>
</ToolTip>
EDIT 2
Here are a few pics from the situation i have now:
the whole element must be shown when tooltip shows.
before tooltip: http://desmond.imageshack.us/Himg832/scaled.php?server=832&filename=beforedo.png&res=landing
when tooltip is shown: http://desmond.imageshack.us/Himg842/scaled.php?server=842&filename=afterbl.png&res=landing
tooltip has too large height and only shows what screens shows. only problem is to 'fiil in' the hidden text.
VisualBrush renders as a bitmap exactly the same thing you are providing by the 'Visual' property, and it does so without any modification to that thing: it renders them exactly as they are now.
If you want to display something else, you have to provide that something else.. Could you try with something like that: ?
<Window x:Class="UncutTooltip.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">
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Width" Value="250" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<TextBlock Text="{Binding TheText}"
TextTrimming="CharacterEllipsis">
</TextBlock>
<Grid.ToolTip>
<TextBlock Text="{Binding TheText}"
TextTrimming="CharacterEllipsis">
</TextBlock>
</Grid.ToolTip>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Border Background="Red" >
<TextBlock Margin="5" Foreground="WhiteSmoke" FontSize="18"
Text="The end of window:)" TextAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90" />
</TextBlock.LayoutTransform>
</TextBlock>
</Border>
</StackPanel>
</Window>
---
using System.Collections.Generic;
using System.Windows;
namespace UncutTooltip
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new List<Item>
{
new Item { TheText = "its not that hard what i want, but i'm pulling my hairs for days!" },
new Item { TheText = "i just want the same tooltip behaviour like the WIndows Explorer: overlay a partially hidden tree/list element with the tooltip that displays the full element" },
new Item { TheText = "i use the following datatemplate in my treeview" },
new Item { TheText = "As you can see, i'm trying to use Visualbrush. but this doesnt work. it only shows what you see on the screen." },
new Item { TheText = "I have tried with static resource and binding on a new stackpanel thats in the tooltip, but that only leaves with a blanc tooltip." },
new Item { TheText = "Do i something wrong? do i have to use alternatives? i'm pretty new in WPF. i know the basics, but binding/resources is kinda new for me" },
};
}
}
public class Item
{
public string TheText { get; set; }
}
}
Edit:
Now, change the tooltip contents to i.e.:
<Grid.ToolTip>
<ListBox ItemsSource="{Binding TheWholeList}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<!--<Setter Property="Width" Value="250" />-->
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<TextBlock Text="{Binding TheText}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid.ToolTip>
and also change the data definition to:
public class Item
{
public string TheText { get; set; }
public IList<Item> TheWholeList { get; set; }
}
var tmp = new List<Item>
{
.........
};
foreach (var it in tmp)
it.TheWholeList = tmp;
this.DataContext = tmp;
Note that I've commented out the width constraint in the tooltip's listbox, it will present an untruncated list of untruncated elements..
Edit #2:
<StackPanel Orientation="Horizontal">
<ListBox x:Name="listbox" ItemsSource="{DynamicResource blah}"> // <---- HERE
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Width" Value="250" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<TextBlock Text="{Binding TheText}" TextTrimming="CharacterEllipsis" />
<Grid.ToolTip>
<ToolTip DataContext="{DynamicResource blah}"> // <---- HERE
<TextBlock Text="{Binding [2].TheText}" /> // <---- just example of binding to a one specific item
<!-- <ListBox ItemsSource="{Binding}"> another eaxmple: bind to whole list.. -->
</ToolTip>
</Grid.ToolTip>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
public class Item
{
public string TheText { get; set; }
}
public MainWindow()
{
InitializeComponent();
Resources["blah"] = new List<Item> // <---- HERE
{
new Item { TheText = ........
........
In the last example, I've changed the window.DataContext binding, to a binding to a DynamicResource. In the window init, I've also changed the way the data is passed to the window. I've changed the tooltip template to include the Tooltip explicitely, and bound it to the same resource. This way, the inner tooltip's textblock is able to read the 3rd row of the datasource directly - this proves it is bound to the list, not to the Item.
However, this is crappy approach. It will work only with explicit Tooltip, only with Tooltip.DataContext=resource, and probably, it is the only working shape of such approach.. Probably it'd be possible to hack into the tooltip with attached behaviours and search it's parent window and get the bindings to work, but usually, it's not worth.. Could you try binding to the Item's properties like in the second sample?

How do I trigger a WPF expander IsExpanded property from another expander's IsExpanded property

I have two expanders, side by side. I want only one to be expanded at a time. So if one is expanded, and the user expands the other, I want the first one to collapse. The user can have both collapsed, and both collapsed is the starting state.
As can be seen in the code, I have included the "Header" property as a test, and it works as expected, but the IsExpanded property is not working.
<Expander x:Name="emailExpander">
<Expander.Style>
<Style TargetType="Expander">
<Setter Property="IsExpanded" Value="False"/>
<Setter Property="Header" Value="Email"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpanded,ElementName=customerExpander}" Value="True">
<Setter Property="IsExpanded" Value="False"/>
<Setter Property="Header" Value="other expanded"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
</Expander>
This can be handled by binding to a view object with a little logic added.
In your WPF bind the IsExpanded property to the EmailExpanded and CustomerExpanded properties of the view.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Expander Grid.Column="0" Header="Email" IsExpanded="{Binding EmailExpanded}">
<TextBlock Text="Email Data"/>
</Expander>
<Expander Grid.Column="1" Header="Customer" IsExpanded="{Binding CustomerExpanded}">
<TextBlock Text="Customer Data"/>
</Expander>
</Grid>
Assign the view in your main Window.
public MainWindow()
{
InitializeComponent();
DataContext = new View();
}
Then make your view class something like the following.
class View : INotifyPropertyChanged
{
private bool _CustomerExpanded;
public bool CustomerExpanded
{
get
{
return _CustomerExpanded;
}
set
{
if (_CustomerExpanded != value)
{
// Add logic to close Email Expander
if (value)
{
EmailExpanded = false;
}
_CustomerExpanded = value;
OnPropertyChanged("CustomerExpanded");
}
}
}
private bool _EmailExpanded;
public bool EmailExpanded
{
get
{
return _EmailExpanded;
}
set
{
if (_EmailExpanded != value)
{
// Add logic to close Customer Expander
if (value)
{
CustomerExpanded = false;
}
_EmailExpanded = value;
OnPropertyChanged("EmailExpanded");
}
}
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
Notice the addition to the setters. Collapsing an expander will have no effect on the other expander, but expanding one will cause the other to collapse. No stack overflow :)
I found the answer in this post:
WPF Expanders Triggers
Use BoolInverterConverter in the answer above and here is the code snippets for your case
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:BoolInverterConverter x:Key="bic"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Expander x:Name="emailExpander" IsExpanded="{Binding ElementName=customerExpander, Converter={StaticResource bic}, Path=IsExpanded}">
<Expander.Style>
<Style TargetType="Expander">
<Setter Property="Header" Value="Email"/>
</Style>
</Expander.Style>
<StackPanel Margin="10,4,0,0">
<CheckBox Margin="4" Content="Email 1" />
<CheckBox Margin="4" Content="Email 2" />
<CheckBox Margin="4" Content="Email 3" />
</StackPanel>
</Expander>
<Expander x:Name="customerExpander" Grid.Column="1">
<Expander.Style>
<Style TargetType="Expander">
<Setter Property="Header" Value="Customer"/>
</Style>
</Expander.Style>
<StackPanel Margin="10,4,0,0">
<CheckBox Margin="4" Content="Customer 1" />
<CheckBox Margin="4" Content="Customer 2" />
<CheckBox Margin="4" Content="Customer 3" />
</StackPanel>
</Expander>
</Grid>
What you're better off doing is use an accordion control released in the WPF Toolkit V2. Very handy and no "Stack Overflow" exceptions. =)

Resources