WPF binding to TextBox MinLines MaxLines - wpf

I have an wpf Expander with templated header. In this template, i have an TextBox, which use Binding with Converter to set MaxLines and MinLines, which depends on Expander.IsExpanded.
The idea is to let user see first line of text and show more when Expander is expanded (alternate solution would be to make that TextBox.Visiblitity = Collapsed when expanded and another TextBox.Visibility = Visible, but user will lost their cursor position, marked text and i dont know what else)
<UserControl>
<UserControl.Resources>
<DataTemplate x:Key="MyTemplate">
<Grid>
<Grid.Resources>
<Converters:ExpandedToLineRowsConverter ExpandedLines="5"
CollapsedLines="1"
x:Key="ExpandedToLines"/>
</Grid.Resources>
<TextBox MaxLines="{Binding IsExpanded,
Converter={StaticResource ExpandedToLines},
RelativeSource={RelativeSource FindAncestor,
AncestorLevel=1,
AncestorType={x:Type Expander}},
UpdateSourceTrigger=PropertyChanged}"
MinLines="{Binding IsExpanded,
Converter={StaticResource ExpandedToLines},
RelativeSource={RelativeSource FindAncestor,
AncestorLevel=1,
AncestorType={x:Type Expander}},
UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>
</UserControl.Resources>
<Expander Header="{Binding}"
HeaderTemplate="{StaticResource MyTemplate}">
<!-- other wpf controls under expander, they do not affect the problem -->
</Expander>
</UserControl>
ExpandedToLineRowsConverter is very simple:
public class ExpandedToLineRowsConverter : IValueConverter
{
public int ExpandedLines { get; set; }
public int CollapsedLines { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? ExpandedLines : CollapsedLines;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
return (int)value != CollapsedLines;
}
}
Problem is, it is working only when expanding, not when collapsing (textbox stay in multiline mode even when Expander.IsExpanded = false).
When i set breakpoint in converter, it is returning corect number of lines, but it looks like the TextBox just ignore them.
I have no idea what to do...
Edit: sample VS2012 project with problem

Related

Highlight item in ItemsControl

I want to render a couple of elements using an ItemsControl, and highlight one of them
My ViewModel:
public class ViewModel
{
public List<Item> Items;
public Item HighlightedItem;
}
My XAML:
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<myUserControl Background="{?}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I want to highlight the item by setting the background property to something specific, how should I go on about it?
First have a converter which will compare reference of two objects say ObjectEqualsConverter
public class ObjectEqualsConverter : IMultiValueConverter
{
#region IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter,
CultureInfo culture)
{
return values[0] == values[1];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
And in XAML file, use converter to check if current item is same as highlighted item in ViewModel and in case converter returns true set the color of control using trigger-
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<myUserControl x:Name="myControl" />
<DataTemplate.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ObjectEqualsConverter}">
<Binding/>
<Binding Path="DataContext.HighlightedItem" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ItemsControl}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter TargetName="myControl" Property="Background" Value="Red"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Make sure you add converter as a resource in your xaml file.

how to edit listview row with a combo

I have a ListView with rows on it. When I click on a particular cell, I want that selected cell to be editable with a combo box and the thing is I have done it, but the combox box still remains even after editing. I want the combo box to change back to textblock.
<Style TargetType="{x:Type FrameworkElement}"
x:Key="GridEditStyle">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Visibility"
Value="{Binding Path=IsSelected,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem}},
Converter={StaticResource boolToVis},
ConverterParameter=True}" />
</Style>
<ComboBox SelectedItem="Present"
ItemsSource="{Binding ElementName=This,
Path=AvailablePublishers}"
Style="{StaticResource GridEditStyle}" />
code behind is
private ObservableCollection<string> _AvailablePublishers =
new ObservableCollection<string>();
public Student_Attendance()
{
InitializeComponent();
_AvailablePublishers.Add("Present");
_AvailablePublishers.Add("Absent");
_AvailablePublishers.Add("Late");
}
public ObservableCollection<string> AvailablePublishers
{ get { return _AvailablePublishers; } }
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
bool param = bool.Parse(parameter as string);
bool val = (bool)value;
return val == param ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
plz help me out
thank you
You know if you used a DataGrid, you would't have to do anything for switching the viewing and editing templates, it's done automatically ... here's a sample to get you started:
<DataGrid ItemsSource="{Binding ...}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock DataContext="{Binding ...}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ElementName=This, Path=AvailablePublishers}" SelectedItem="{Binding ...}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

Expand/Collapse all expanders

I have a list of expanders which I want to control its expanded state(IsExpanded) with global toggle button which should toggle between expanded/collapsed state.
The solution which I have got so far does that by binding the expander's IsExpanded state to the togglebutton's IsChecked state. This works as long as I dont manually dont toggle the expanders. Once I do that those particular expanders dont respect the binding (toggle button's IsChecked state).
Any idea why? and is there a clean solution in XAML for this?
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<StackPanel>
<ToggleButton Name="ExpandAll">Toggle</ToggleButton>
<Expander IsExpanded="{Binding ElementName=ExpandAll,Path=IsChecked, Mode=OneWay}">
Hai
</Expander>
<Expander IsExpanded="{Binding ElementName=ExpandAll,Path=IsChecked, Mode=OneWay}">
Hello
</Expander>
<Expander IsExpanded="{Binding ElementName=ExpandAll,Path=IsChecked, Mode=OneWay}">
Weird
</Expander>
</StackPanel>
</Grid>
</Page>
I know that this post is very old. Just posting this for anyone else who comes across. The below code worked for me.
<Expander IsExpanded="{Binding ElementName=ExpandAll, Path=IsChecked, UpdateSourceTrigger=Explicit}">
</Expander>
This works when the expanders are generated dynamically, for example inside a DataGrid.RowDetailsTemplate.
I don't think you can achieve this entirely in XAML, but the following allows you to do it with an IValueConverter:
<StackPanel>
<StackPanel.Resources>
<local:Converter x:Key="Converter" />
</StackPanel.Resources>
<ToggleButton Name="ExpandAll">
<ToggleButton.IsChecked>
<MultiBinding Mode="OneWayToSource" Converter="{StaticResource Converter}">
<Binding ElementName="Expander1" Path="IsExpanded" />
<Binding ElementName="Expander2" Path="IsExpanded" />
<Binding ElementName="Expander3" Path="IsExpanded" />
</MultiBinding>
</ToggleButton.IsChecked>
Toggle</ToggleButton>
<Expander Name="Expander1">
Hai
</Expander>
<Expander Name="Expander2">
Hello
</Expander>
<Expander Name="Expander3">
Weird
</Expander>
</StackPanel>
And your Converter is as below:
public class Converter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//we're using OneWayToSource, so this will never be used.
return DependencyProperty.UnsetValue;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
// we want to convert the single 'IsChecked' value from the ToggleButton into
// 3 'IsExpanded' values
var allValues = new object[targetTypes.Length];
for (int i = 0; i < allValues.Length; i++)
{
allValues[i] = value;
}
return allValues;
}
}
This works by setting up a OneWayToSource binding between the IsChecked property of the ToggleButton (i.e. the binding will be set against the source when the target value changes), and uses an IMultiValueConverter to translate the single value into one for each of the Expanders.

Turn off and on column visibility

I have a DataGrid with many columns and I'd like to provide the users with a drop down that allows them to select which columns they can see. I'm using the .Net 4 WPF DataGrid in a desktop application.
Does anyone know of an easy way to accomplish what I am trying to do.
I do this as follows.
I derive from the grid an add an ICommand called HideShowColumnCommand that takes as its parameter a DataGridColumn (the one I want to hide or show) and hides the column if it is visible, and shows it if it is not.
Then I use a tricky context menu that I attach to the column header that has a tick that shows the column visible/hidden state..
The context menu looks like so
<ContextMenu
ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Column.DataGridOwner.Columns}">
<ContextMenu.Resources>
<local:DataGridHeaderVisibilityToBooleanConverter
x:Key="visibilityConverter" />
<BooleanToVisibilityConverter
x:Key="VisibilityOfBool" />
<DataTemplate
DataType="{x:Type DataGridColumn}">
<ContentPresenter
Content="{Binding Path=Header}"
RecognizesAccessKey="True" />
</DataTemplate>
</ContextMenu.Resources>
<ContextMenu.ItemContainerStyle>
<Style
TargetType="MenuItem">
<!--Warning dont change the order of the following two setters
otherwise the command parameter gets set after the command fires,
not much use eh?-->
<Setter
Property="CommandParameter"
Value="{Binding Path=.}" />
<Setter
Property="Command"
Value="{Binding Path=DataGridOwner.HideShowColumnCommand}" />
<Setter
Property="IsChecked"
Value="{Binding Path=Visibility, Converter={StaticResource visibilityConverter}}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
The converter like this
public class DataGridHeaderVisibilityToBooleanConverter :IValueConverter{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
try {
Visibility visibility = (Visibility)value;
if (visibility == Visibility.Visible) {
return true;
}
else {
return false;
}
}
catch { }
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
#endregion
}

Show the validation error template on a different control in WPF

I have a UserControl that contains other controls and a TextBox. It has a Value property that is bound to the TextBox text and has ValidatesOnDataErrors set to True.
When a validation error occurs in the Value property binding, the error template (standard red border) is shown around the entire UserControl.
Is there a way to show it around the TextBox only?
I'd like to be able to use any error template so simply putting border around textbox and binding its color or something to Validation.HasError is not an option.
Here's my code:
<DataTemplate x:Key="TextFieldDataTemplate">
<c:TextField DisplayName="{Binding Name}" Value="{Binding Value, Mode=TwoWay, ValidatesOnDataErrors=True}"/>
</DataTemplate>
<controls:FieldBase x:Name="root">
<DockPanel DataContext="{Binding ElementName=root}">
<TextBlock Text="{Binding DisplayName}"/>
<TextBox x:Name="txtBox"
Text="{Binding Value, Mode=TwoWay, ValidatesOnDataErrors=True}"
IsReadOnly="{Binding IsReadOnly}"/>
</DockPanel>
UserControl (FieldBase) is than bound to ModelView which performs validation.
to accomplish this task I've used this solution. It uses converter, that "hides" border by converting (Validation.Errors).CurrentItem to Thickness.
<Grid>
<Grid.Resources>
<data:ValidationBorderConverter
x:Key="ValidationBorderConverter" />
</Grid.Resources>
<Border
BorderBrush="#ff0000"
BorderThickness="{Binding
ElementName=myControl,
Path=(Validation.Errors).CurrentItem,
onverter={StaticResource ValidationBorderConverter}}">
<TextBox
ToolTip="{Binding
ElementName=myControl,
Path=(Validation.Errors).CurrentItem.ErrorContent}" />
</Border>
</Grid>
ValidationBorderConverter class is pretty simple:
[ValueConversion(typeof(object), typeof(ValidationError))]
public sealed class ValidationBorderConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return (value == null) ? new Thickness(0) : new Thickness(1);
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

Resources