Top Dock in a DockPanel is taking all the space - c#

My code looks something like this:
<DockPanel>
<Expander DockPanel.Dock="Top" Name="Expander1">
<local:ListView1 DataContext="{Binding Source1}"/>
</Expander>
<Expander DockPanel.Dock="Top" Name="Expander2">
<local:ListView1 DataContext="{Binding Source2}"/>
</Expander>
<Expander DockPanel.Dock="Top" Name="Expander3">
<local:ListView1 DataContext="{Binding Source3}"/>
</Expander>
</DockPanel>
ListView1 is just a user control that contains a ListView
I have the behavior set: when one Expander is open, all the other 2 Expanders will close.
The problem is that when Expander1 is open and its content is more than the window height, it will have a scroll bar to scroll down for its content while Expander2 and Expander3 are not displayed. I think Expander1 uses all the space on the UI and Expander2 & Expander3 get pushed out side of the UI. When Expander2 is open, Expander3 is pushed out of the UI and not displayed. What can I do so that when I open an Expander, the one(s) below it won't get pushed out of the UI?

You said you're only allowing one open at a time.
Given that then the size logic is simpler and you could use just a converter with a fixed number.
My PoC:
<DockPanel>
<Expander DockPanel.Dock="Top" MaxHeight="{Binding ActualHeight,
RelativeSource={RelativeSource AncestorType=ContentPresenter},
Converter={local:AddConverter ValueToAdd=-46}}">
<ListBox ItemsSource="{Binding Items}"/>
</Expander>
<Expander DockPanel.Dock="Top" MaxHeight="{Binding ActualHeight,
RelativeSource={RelativeSource AncestorType=ContentPresenter},
Converter={local:AddConverter ValueToAdd=-46}}">
<ListBox ItemsSource="{Binding Items}"/>
</Expander>
<Expander DockPanel.Dock="Top" MaxHeight="{Binding ActualHeight,
RelativeSource={RelativeSource AncestorType=ContentPresenter},
Converter={local:AddConverter ValueToAdd=-46}}">
<ListBox ItemsSource="{Binding Items}"/>
</Expander>
</DockPanel>
And the converter
public class AddConverter : MarkupExtension, IValueConverter
{
public double ValueToAdd { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double bound = (Double)value;
return bound + ValueToAdd;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
As I mentioned in the comments.
You could make this more sophisticated with a multibinding and multiconverter. That could allow for height when 2 are open.

Related

WPF - Conditional binding in ItemsControl

I'm very new to WPF, and am just getting started with data binding. What I'd like to do is generate a list of checkboxes based on a list in my view model. The XAML I have at the moment is:
<ItemsControl ItemsSource="{Binding Path=TestList, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Path=Name}" IsChecked="{Binding Path=Enabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10,5,10,5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This works correctly, and generates a checkbox for every item in TestList. What I'd like to do is only generate checkboxes for items where the condition TestList[i].Type == "Mode" is true. I believe that I may need to use a <DataTrigger> element, but I don't know the details of how to do this.
[EDIT] Just to clarify, each element of TestList has Name, Enabled, and Type properties.
There are several ways to do this. However, the simplest static approach would be to just filter it at your ViewModel
Filtered = new ObservableCollection(TestList.Where(x => x.Type == "Mode"));
...
<ItemsControl ItemsSource="{Binding Path=Filtered , UpdateSourceTrigger=PropertyChanged}">
Note : There are fancier more dynamic ways to achieve this, though this might help you out
As I guess that you want to make the checkbox appear if TestList.Type changes, I would suggest make a Converter and Bind it to the CheckBox Visibility.
public sealed class CheckBoxVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || parameter == null)
return Visibility.Visible;
var type = (string)value;
var condition = (string)parameter;
return type.Equals(condition) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
And then in the dictionary add the reference to your namespace
xmlns:converters="clr-namespace:Projct.Converters;
and in the resources dictionary
<converters:CheckBoxVisibilityConverter x:Key="CheckBoxConverter"/>
Finally in the xaml
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox
Margin="10,5,10,5"
Content="{Binding Path=Name}"
IsChecked="{Binding Path=Enabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding Path=Type, Converter={StaticResource CheckBoxConverter}, ConverterParameter=Mode}" />
</DataTemplate>
</ItemsControl.ItemTemplate>

WPF - Disable tooltip when value is null or empty

I have this problem, i'm using telerik on WPF and a source on my grid and doing the correct bindings. I want to be able to disable the tooltip when the value of the binding is null or empty. How can it be done?
This is one example:
<telerik:GridViewDataColumn x:Name="GRIDVIEWCOLUMN_ENDDATE" Header="Data de Conclusão" DataMemberBinding="{Binding ClosedDate, StringFormat=dd-MM-yyyy}" IsVisible="False" Width="auto" IsFilterable="False">
<telerik:GridViewDataColumn.ToolTipTemplate>
<DataTemplate>
<TextBlock Text="{Binding ClosedDate, StringFormat=dd-MM-yyyy}" FontFamily="Segoe UI Light" FontSize="13.667" />
</DataTemplate>
</telerik:GridViewDataColumn.ToolTipTemplate>
</telerik:GridViewDataColumn>
Just bind the Visibility of your tooltip (in this case, you have provided a TextBlock) to the same property - ClosedDate, and use a converter to get the value based on your logic.
public class TooltipVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (value is string)
{
return String.IsNullOrEmpty(value as string) ? Visibility.Collapsed :Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
In your XAML, you would have something like this. You declare your converter as a static resource somewhere in your view, and later refer to it in your binding.
<converters:TooltipVisibilityConverter x:Key="TooltipVisibilityConverter"/>
<telerik:GridViewDataColumn x:Name="GRIDVIEWCOLUMN_ENDDATE" Header="Data de Conclusão" DataMemberBinding="{Binding ClosedDate, StringFormat=dd-MM-yyyy}" IsVisible="False" Width="auto" IsFilterable="False">
<telerik:GridViewDataColumn.ToolTipTemplate>
<DataTemplate>
<TextBlock Text="{Binding ClosedDate, StringFormat=dd-MM-yyyy}" FontFamily="Segoe UI Light" FontSize="13.667" Visibility="{Binding ClosedDate, Converter={StaticResource x:Key="TooltipVisibilityConverter"}" />
</DataTemplate>
</telerik:GridViewDataColumn.ToolTipTemplate>
I know this is a pretty old question but I was trying to hide empty tooltips and found a much simpler way to do so here: http://wpfthoughts.blogspot.com/2014/02/hiding-empty-tooltips.html.
Basically if you put a resource dictionary in App.xaml you can automatically hide all empty/null tooltips in your application.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Style TargetType="{x:Type ToolTip}">
<Style.Triggers>
<Trigger Property="Content" Value="{x:Static sys:String.Empty}">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="Content" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
It is overkill if you only need it in one spot but it's a super simple fix that prevents you from having to add code to every page that has tooltips. Hopefully this helps someone out.
You can consider using a IValueConverter to show/hide the tooltip border
<telerik:GridViewDataColumn x:Name="GRIDVIEWCOLUMN_ENDDATE" Header="Data de Conclusão" DataMemberBinding="{Binding ClosedDate, StringFormat=dd-MM-yyyy}" IsVisible="False" Width="auto" IsFilterable="False">
<telerik:GridViewDataColumn.ToolTipTemplate>
<DataTemplate>
<Border Background="Black" Visibility="{Binding ClosedDate, Converter={StaticResource BorderVisible}}" >
<TextBlock Text="{Binding ClosedDate, StringFormat=dd-MM-yyyy}" FontFamily="Segoe UI Light" FontSize="13.667" />
</Border>
</DataTemplate>
</telerik:GridViewDataColumn.ToolTipTemplate>
</telerik:GridViewDataColumn>
class BorderVisibilitySetter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//check if the control's content property is null or empty
if(value == null || value.ToString() == string.Empty)
return Visibility.Collapsed;
else
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Setting the tooltip parent control IsEnabled to false will effectively prevent the tooltip to pop unless ToolTipService.ShowOnDisabled is set to true.
Check george.zakaryan's answer, but instead of using a converter to bind the visibility of the tooltip textblock to its content, use a similar converter to Bind the IsEnabled property of the tooltip's parent to its Textblock's text.
public class StringToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string)
{
return String.IsNullOrEmpty(value as string) ? false : true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
in your xaml
<UserControl.Resources>
<utils:StringToBoolConverter x:Key="StringToBoolConverter"/>
</UserControl.Resources>
and
<TextBlock Text="{Binding SrcDrive, Mode=OneWay}" IsEnabled="{Binding SrcDrive, Converter={StaticResource StringToBoolConverter}, Mode=OneWay}" Width="400" >
<TextBlock.ToolTip>
<TextBlock Text="{Binding SrcDrive, Mode=OneWay}" />
</TextBlock.ToolTip>
</TextBlock>
Native Telerik tooltip for GridView has a defect, which is even when you set Visibility=Collapsed it will still show an empty box (see below image):
Code:
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Name}">
<telerik:GridViewColumn.ToolTipTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Visibility="Collapsed" />
</DataTemplate>
</telerik:GridViewColumn.ToolTipTemplate>
</telerik:GridViewDataColumn>
</telerik:RadGridView.Columns>
Result:
Solution:
Instead of using null check Converter, the approach that worked for me was to skip showing the tooltip when TextBlock.ToolTipOpeningEvent is triggering.
To do so in your xaml.cs do the following:
public partial class MainWindow: Window
{
public MainWindow()
{
InitializeComponent();
EventManager.RegisterClassHandler(typeof(GridViewCell),
TextBlock.ToolTipOpeningEvent,
new RoutedEventHandler(OnToolTipOpening));
}
private void OnToolTipOpening(object sender, RoutedEventArgs e)
{
if (sender is GridViewCell cell) // show tooltip only when text is trimmed
e.Handled = !IsTextTrimmed(cell);
}
static bool IsTextTrimmed(GridViewCell cell) => cell?.Value.ToString().Length > 50;
}
That's it, and no need to write any additional logic to show/hide tooltips.

ToolTip use to show the index of an item from a collection. C# WPF MVVM

I'm plotting a histogram of a grayscale image using an array of 256 values. Im doing so by creating my own chart with 256 vertical rectangles (columns). My aim is that when I mouse over one rectangle I get its index value, its index from the array it lies in, like if I mouse over the 200th rectangle I get 200 in a small text box near cursor, like how ToolTip should perform. The problem is that I don't find the proper binding for ToolTip to get this working. I think the solution lies in the proper use of AlternationCount / AlternationIndex
Here is may XAML code, maybe someone can give me a fix for it:
<ItemsControl Grid.Row="1" ItemsSource="{Binding HistogramValues}" AlternationCount="{Binding Path=HistogramValues.Count}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Height="{Binding }" ToolTip="{Binding AlternationIndex, RelativeSource={RelativeSource AncestorType=ItemsControl}}" Width="2" VerticalAlignment="Bottom" Fill="Black"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
where in view model I have:
public float[] HistogramValues { get; set; }
I've found this useful post
Numbered listbox
but I still cant make it run for my case, I'm confused about that ItemTemplate and TemplatedParent which I dont know if I need or if I need the template how should I code this.
If you are not ok with converters you can use this,
<ItemsControl Grid.Row="1" ItemsSource="{Binding Collection}" AlternationCount="{Binding Path=Collection.Count}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" ToolTip="{Binding Path=(ItemsControl.AlternationIndex), RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentPresenter}}">
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Since AlternationIndex is attached property, it should be covered with "()".
If you dont specify source in a binding, it always binds to datacontext.
Do this
ToolTip="{Binding (ItemsControl.AlternationIndex), RelativeSource={RelativeSource AncestorType=ContentPresenter}}"
If showing the index is your only requirement you can achieve this like below,,
MyConverter is a multiconverter ,and include that in resources
<ItemsControl Grid.Row="1" ItemsSource="{Binding Collection}" >
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Control.ToolTip">
<Setter.Value>
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Path="IsMouseOver" RelativeSource="{RelativeSource Self}"/>
<Binding />
<Binding Path="ItemsSource" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ItemsControl}"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
public partial class MainWindow : Window
{
private ObservableCollection<string> collection;
public ObservableCollection<string> Collection
{
get { return collection; }
set { collection = value; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
collection = new ObservableCollection<string>();
collection.Add("First");
collection.Add("Second");
collection.Add("Third");
collection.Add("Fourth");
}
}
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((bool)values[0])
{
return (values[2] as ObservableCollection<string>).IndexOf(values[1].ToString());
}
else
return "";
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

XAML ListBox - How to change style of last item only?

I have a RadDataBoundListBox (from Telerik) that represents the items of a List. Each item is separated by a bottom line x:Name="ItemSeparatorBorder". The ListBox itself has a header and a footer containing a line too (x:Name="ListTopBorder"and x:Name="ListBottomBorder"). Now I need a way to disable the line (x:Name="ItemSeparatorBorder") of the last item in this ListBox.
I thought about some Visibility binding to x:Name="ItemSeparatorBorder" with a Converter that matches the index of the current item to the total count of the ListBox. But I don't know how to implement it and I can't find any good sample.
The code should work on Windows Phone 8.0 / .NET 4.0.
This is my code so far:
<telerikPrimitives:RadDataBoundListBox
x:Name="ListBox"
ItemsSource="{Binding Items}">
<telerikPrimitives:RadDataBoundListBox.ListHeaderTemplate>
<DataTemplate>
<Grid Height="30">
<Border
x:Name="ListTopBorder"
Height="1"
VerticalAlignment="Bottom"
Background="Blue"/>
</Grid>
</DataTemplate>
</telerikPrimitives:RadDataBoundListBox.ListHeaderTemplate>
<telerikPrimitives:RadDataBoundListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="1"></RowDefinition>
</Grid.RowDefinitions>
<controls:ListItem Margin="30,10,0,10">/>
<Border
x:Name="ItemSeparatorBorder"
Grid.Row="1"
Height="1"
Background="Blue"
Margin="30,0,0,0"/>
</Grid>
</DataTemplate>
</telerikPrimitives:RadDataBoundListBox.ItemTemplate>
<telerikPrimitives:RadDataBoundListBox.ListFooterTemplate>
<DataTemplate>
<Grid Height="30">
<Border
x:Name="ListBottomBorder"
Height="1"
VerticalAlignment="Top"
Background="Blue"/>
</Grid>
</DataTemplate>
</telerikPrimitives:RadDataBoundListBox.ListFooterTemplate>
</telerikPrimitives:RadDataBoundListBox>
How can I hide the Border of the last item?
To make it more clear, I want to remove the last blue line here:
I think the best approach is to use an IValueConverter that gets a reference to the items collection. Make a converter that inherits DependencyObject, and give it a property for the source list. Then you can check the current item index:
public class ItemToVisibilityConverter : DependencyObject, IValueConverter
{
public IList Items
{
get { return (IList )GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty=
DependencyProperty.Register("Items", typeof(IList), typeof(ItemToVisibilityConverter), new PropertyMetadata(null));
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool hide = Items != null
&& value != null
&& Items.IndexOf(value) == Items.Count - 1;
return (hide ? Visibility.Collapsed : Visibility.Visible);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException;
}
}
The put a converter instance in the control's resources:
<telerikPrimitives:RadDataBoundListBox
x:Name="ListBox"
ItemsSource="{Binding Items}">
<telerikPrimitives:RadDataBoundListBox.Resources>
<ResourceDictionary>
<local:ItemToVisibilityConverter x:Key="ItemToVisibilityConverter"
Items="{Binding Items}" />
</ResourceDictionary>
</telerikPrimitives:RadDataBoundListBox.Resources>
...
</telerikPrimitives:RadDataBoundListBox>
Finally, in the "ItemTemplate", bind to the current item using the converter:
<Border x:Name="ItemSeparatorBorder"
Visibility="{Binding Converter={StaticResource ItemToVisibilityConverter}}"
... />

How to implement a converter in WP8 using XAML and C# for capitalize the first letter of a word in a TexBlock?

I have a data template with a TexBlock in XAML. This TexBlock shows a word in a word list. Every word I want to put the first letter capitalized, because all words are in lowercase.
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="AddrBookItemTemplate">
<StackPanel VerticalAlignment="Top">
<TextBlock Margin="5,0,0,0" FontSize="20" Text="{Binding name}" />
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
In c# implement the converter
namespace Converter.ViewModels
{
public class ToCapitalizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return char.ToUpper(value.ToString()[0]) + value.ToString().Substring(1);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value as string).ToLower();
}
}
}
In App.xaml
...
xmlns:vm="clr-namespace:Converter.ViewModels"
<Application.Resources>
<vm:ToCapitalizeConverter x:Key="ToCapitalizeConverter"/>
</Application>
In MainPage.xaml
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="AddrBookItemTemplate">
<StackPanel VerticalAlignment="Top">
<TextBlock Margin="5,0,0,0" FontSize="20" Text="{Binding name, Converter={StaticResource ToCapitalizeConverter}}" />
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
You can use a converter as follows:
<TextBlock Margin="5,0,0,0" FontSize="20" Text="{Binding name, Converter ={StaticResource myConverter}}" />
Specific information on how to implement a converter can be found here. You can essentially perform any operation you like on the text. I actually like Humanizer to do these type of text conversions.

Categories

Resources