In my application I want to switch between editing and just displaying data in my view.
In order to edit them I have textboxes. For Displaying I am using textblock.
Some of the Data is displayed inside a ListView.
In my ViewModel I have variables called "IsEditingMode" and "IsNotEditingMode" to determine the state of the view.
Now I want something like this in wpf:
<switch betwen implementation>
<use textbox if IsEditingMode>
<use textblock if IsNotEditingMode>
I tried this using Converters to change the Visibility of the textbox and the textblock:
Converter:
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var boolValue = (bool)value;
if (boolValue)
return Visibility.Visible;
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Provide the Converter a Resource:
<helper:BoolToVisibilityConverter x:Key="IsVisibleConverter"/>
Use the converter to change the Visibility of the elements in my xaml:
<StackPanel>
<TextBlock Text ="{Binding TextVariableInVM}" Visibility="{Binding IsNotEditingMode, Converter={StaticResource IsVisibleConverter}}"/>
<TextBox Text ="{Binding TextVariableInVM}"
Visibility="{Binding IsEditingMode,
Converter={StaticResource IsVisibleConverter}}"/>
</StackPanel>
The result is:
Editing mode: The Textbox is displayed
Not editing mode: The TextBlock is displayed
For a small number of displayed variables this is just fine.
In my real application I have a huge amount of displayed variables and some of them are displayed using listview.
In that case my UI is getting really slow.
I believe that this is due to the fact that I bind each variable twice.
Is there a more elegant solution to do that editing <-> not editing switch?
Why not use only a TextBox element for both situations?
TextBox has a property called IsReadOnly that you can set to true or false depending on your action.
Is this code enough for your need's?
It's a TextBox that binds your text, and changes from edit mode or not.
<TextBox Text="{Binding TextVariableInVM}" IsReadOnly="{Binding IsNotEditingMode}"/>
If you complain about UI being slow, this example will be faster and more elegant because:
Doesn't need a StackPanel
One text element instead of two
Just one extra property needed to check if is editable or not
Doesn't need to convert from Boolean to Visibility
You may also want to remove the TextBox Border when it is read-only by a Style like this:
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="BorderBrush" Value="{x:Null}"/>
</Trigger>
</Style.Triggers>
</Style>
Related
I understand the concept of Binding / Value converter well, but for some reasons the following binding doesn't work. I would like the change the FontWeight to Bold for some Descriptions (Description is a text field):
XAML:
<DataGridTextColumn Header="Description"
Binding="{Binding Description}"
FontWeight="{Binding Description, Converter={converters:DescriptionToFontWeightConverter}}"/>
Value converter method (simplified):
public class DescriptionToFontWeightConverter : ConverterMarkupExtension<DescriptionToFontWeightConverter>
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// Some logic based on the value
// ..
return "Bold"; // I believe I should use "Bold", and not "FontWeights.Bold" here (like it would be with a dependency property, but the problem is that it doesn't go inside the method.
}
public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => Binding.DoNothing;
}
I believe the problem comes from the binding path of the FontWeight property. For example, if I replace (Path=)Description by RelativeSource={RelativeSource Self}, it goes inside the value converter method, but I don't think I can retrieve the value of the binding.. I think it isn't something abnormal (at least something I didn't expect), but I wonder if I shouldn't replace the DataGridTextColum by a DataGridTemplateColum and digs further?
I use the ConvertMarkupExtension method from this website which doesn't require to specify the value converters as static resources.
UPDATE with DataGridColumnTemplate
<DataGridTemplateColumn Header="Description">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontWeight"
Value="{Binding Description, Converter={converters:DescriptionToFontWeightConverter}}"/>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Description}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
Thanks for any insights :-)
A DataGridColumn doesn't inherit any DataContext so trying to bind to a Description property of the current item will always fail.
The Binding property is special. Its type is Binding and the binding that you define in XAML will eventually be applied to the element that gets created at runtime. In the case of a DataGridTextColumn, this is a TextBlock or a TextBox depending on whether you are in edit mode.
but I wonder if I shouldn't replace the DataGridTextColum by a DataGridTemplateColum and digs further
If you use a DataGridTemplateColumn and define a TextBlock in the CellTemplate (and a TextBox in the CellEditingTemplate), you can actually bind to a property of the current item as usual. This is because the element in the template is added to the element tree and inherits a DataContext like any other element.
I have a button control in a WPF application which is used for log-in purposes. The button is dual purpose: when logged-out, it shall display the text "log in". When logged-in, it shall display the text "log out".
I could do this with databinding to an appropriate string property in the viewmodel.
But it would be neater if I could simply databind to the "loggedIn" boolean (false for logged-out, true for logged-in) and then make the decision about what text to display on the button from within the view.
Is this possible?
You can achieve it using Style in Xaml without writing specific C# code.
Suppose you have IsLoggedIn property in ViewModel which is being changed on button bind command's execute method :
private void MyButtonCommandExecuteMethod()
{
this.IsLoggedIn = !this.IsLoggedIn;
}
private bool isLoggedIn;
public bool IsLoggedIn
{
get
{
return this.isLoggedIn;
}
set
{
this.isLoggedIn = value;
OnPropertyChanged(nameof(IsLoggedIn));
}
}
In the above code OnPropertyChanged is method implementation of INotifyPropertyChanged's PropertyChanged event. If you using any framework or defined own name, then replace it with appropriate name.
You could define a style such as:
<Button Command="{Binding YourButtonCommand}">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Content" Value="Log In" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoggedIn}" Value="True">
<Setter Property="Content" Value="Log out" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
In the above code IsLoggedIn is bind to DataTrigger to update the Button's Content property based on it's value.
Another point, the button style is inherited from default style. If you any Keyed style then use it in BasedOn Property.
Solution
Yes that is possible using value converter, specifically IValueConverter. You can bind a bool property, say BoolProperty from your viewmodel to button using converter like this.
<Button Content="{Binding BoolProperty, Converter={StaticResource BoolConverter}}" />
Step 1: You need to create a converter that will accept bool value and return whatever string as you wish.
public class BoolConverter : System.Windows.Data.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool)
{
return value.ToString();
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Step 2: Declare this converter as a resource in XAML (you will need to define local namespace alias that contains the converter in your <Window ..> start element.
<local:BoolConverter x:Key="BoolConverter" />
So now whenever property changed event is raised for BoolProperty, this converter will be triggered and button text will change appropriately.
Suggestion:
I am not sure why you think maintaining extra string property is not a clean way. It is clean and is much simpler approach than using converter. Whatever I have shown is unnecessary overhead for your case, IMO. However, since you asked about possibility, I detailed it. Choice is yours! :)
I have a control which has a default value for a property. When the control first gets its dataContext set, it assigns this property automatically.
In the xaml now, I want it to be possible to UNset this property. I've tried setting it to x:Null of just the empty string, but then I get an error because there's no converter for the property. How do I simply unassign this property from the xaml in the rare cases where I want the feature disabled?
code where it is originally set:
void OmniBox_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if( e.NewValue is BindingObjectBaseExtended )
{
BindingObjectBaseExtended value = (BindingObjectBaseExtended)e.NewValue;
this.SetBinding(OmniBox.ContextValidationMessagesProperty, new Binding() { Source = value, Path = new PropertyPath("ValidationMessages") });
}
}
xaml where I want to unset the property.
<Style TargetType="ui:OmniBox">
<Setter Property="ContextValidationMessages" Value="" />
</Style>
Note that if I do not set up the binding automatically when the data context changes, then by default there are no validation messages and I have to do the following in the xaml to set them up:
<Style TargetType="ui:OmniBox">
<Setter Property="ContextValidationMessages" Value="ValidationMessages" />
</Style>
What I'm trying to do is make the above binding the default for my custom OmniBox control, and allow the user to unset it or change it to something else.
DependencyProperty.UnsetValue cannot be used in XAML.
http://msdn.microsoft.com/en-us/library/system.windows.dependencyproperty.unsetvalue(v=vs.90).ASPX
Personally, I would create a separate dependency property, such as bool AutoBindValidation and make it default to true. If it is false, don't do anything when the DataContext changes. This is a little more self-documenting. Depending on what exactly you're trying to do, you might not want to publicly expose ContextValidationMessages at all.
If you really want to do it the way you posted, I'm not sure why setting it to {x:Null} would cause an error (unless the property type is not nullable). But this approach would have problems because DataContextChanged is going to occur after the XAML is parsed. So the user can set it to {x:Null}, but then the DataContext will change and your code will set up the default binding and trample the user's value. You could set up the binding in the control's contstructor, but then if the DataContext does not have a ValidationMessages property, your control will be spitting out binding errors.
This may be impossible, my best bet was this:
<Setter Property="ContextValidationMessages"
Value="{x:Static DependencyProperty.UnsetValue}" />
But that throws "Cannot unset setter value". So you better inverse your logic or keep the property unset another way.
I don't think there is any supported way to do this in the xaml itself. In your code you are setting a local value on the ContextValidationMessagesProperty. The Style setters you included would have a lower dependency property precedence and even if they were evaluated they would set a value based on the specified Value - not clear it. Maybe instead of setting the binding in code you could have a Setter in your default style for OmniBox that sets that property - e.g.
<Setter Property="ContextValidationMessages" Value="{Binding ValidationMessages}" />
If you have to conditionally set the Binding then you could create a custom IValueConverter that checks for the specified type (passed as the parameter). e.g.
public class IsAssignableFromConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Type typeParameter = parameter as Type;
if (typeParameter == null)
return DependencyProperty.UnsetValue;
return value != null && typeParameter.IsAssignableFrom(value.GetType());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
Then you might use it like this:
<local:IsAssignableFromConverter x:Key="isAssignableConverter" />
<Style TargetType="ui:OmniBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource isAssignableConverter}, ConverterParameter={x:Type ui:BindingObjectBaseExtended}}" Value="True">
<Setter Property="ContextValidationMessages" Value="{Binding ValidationMessages}" />
</DataTrigger>
</Style.Triggers>
</Style>
In the case where you don't want this property to be applied you might set the Style for that instance of the OmniBox to a new style and make sure to set the OverridesDefaultStyle property to true.
I suppose another option is to create another dependency property that will call ClearValue on the ContextValidationMessages property but this seems like it could be a maintenance issue.
For certain cases you can 'reset' to the default value of the parent control by using a RelativeSource. For instance I'm using a DataGrid and this worked for me to reset back to the 'default'.
This is a textblock inside a datagrid cell.
<TextBlock Text="{Binding ServiceName}">
<TextBlock.Style>
<Style>
<Style.Triggers>
<!-- Change text color to purple for FedEx -->
<Trigger Property="TextBlock.Text" Value="FedEx">
<Setter Property="TextBlock.Foreground" Value="Purple"/>
</Trigger>
<!-- Reset if the cell is selected, since purple on blue is illegible -->
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}}" Value="True">
<Setter Property="TextBlock.Foreground" Value="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
This seems clever enough to inherit the correct color even when the window is inactive.
I have a scenario where I need to specify Functions like
void SomeFunction(int value)
For this I'm using two DataGrids.
The left DataGrid holds the Functions
The right DataGrid holds the Parameters for the selected Function
I only want the Parameter DataGrid to be enabled when a valid Function is selected in the Function DataGrid. If the NewItemPlaceHolder (the Last Row when CanUserAddRows="True" for a DataGrid) is selected or if the selection is empty I want it to be disabled. I experimentet with a DataTrigger but I couldn't get it to work
<Style TargetType="DataGrid">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=functionDataGrid,
Path=SelectedItem}"
Value="{x:Type systemData:DataRowView}">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
Is it possible to check if the value produced by the Binding is of a specific Type? Otherwise, does anyone have any other solutions to this? As of now, I'm handling this with the SelectedCellsChanged event but I would prefer not using code behind
Thanks
If anyone else comes across the same problem, here's what I did to solve it. I created a TypeOfConverter that returns the Type of the value produced by the Binding.
<DataTrigger Binding="{Binding ElementName=functionsDataGrid,
Path=SelectedItem,
Converter={StaticResource TypeOfConverter}}"
Value="{x:Type data:DataRowView}">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
TypeOfConverter
public class TypeOfConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value == null) ? null : value.GetType();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Have you considered a DataTemplate for the right DataGrid (parameters)?
Then you could bind the DataContext of your right DataGrid to the SelectedItem of the left DataGrid.
And in your DataTemplate, you can make your right side look like an enabled DataGrid parameter entry form when the DataTemplate's DataType={x:Type local:FunctionObject}. When a 'FunctionObject' is not selected you can have a DataTemplate for that too which shows a disabled DataGrid parameter entry form, or you could choose to display nothing as well.
I'm trying to add some default text to combo boxes that will show when there is no item selected. I'm using a style to acheive this which works great when the combo is first loaded.
<Style TargetType="{x:Type igRibbon:ComboEditorTool}" x:Key="PleaseSelect">
<Style.Triggers>
<Trigger Property="SelectedIndex" Value="-1">
<Setter Property="Text" Value="Please Select" />
</Trigger>
</Style.Triggers>
</Style>
<igRibbon:ComboEditorTool Style="{StaticResource PleaseSelect}"
ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem }" />
But when the combo's selected item is reset (by setting it to null, which sets the SelectedIndex to -1) it fails to display the default text (even though the the trigger does fire), what could be the reason for this? Is there a better way to reset the selected item?
Cheers
Here is the solution that I used, thanks to #AlexPaven for the idea:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
{
return "Please Select";
}
else
{
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string && ((string)value) == "Please Select")
{
return null;
}
else
{
return value;
}
}
I'm not familiar with the Infragistics suite, but I suspect it's the same thing as with regular combo boxes: since you have a binding on SelectedItem, the Text cannot be set to an item that blatantly disobeys that binding; the Text IS a representation of the SelectedItem. If the SelectedItem is null, then the Text must also be a representation of null.
I'm guessing (didn't try it and I may be just plain wrong) you could probably accomplish this with an IValueConverter that returns a custom string when the passed object is null (and returns the object unchanged otherwise), set on the SelectedItem binding.
It's been a while since this question was asked, but let me answer it with a more Infragistics related answer.
Let's start with a short side node:
We have to be careful when using XamComboEditor without specifying the namespace,
because in the Infragistics Framework the class is defined two times.
1. Infragistics.Windows.Editors.XamComboEditor
2. Infragistics.Controls.Editors.XamComboEditor
Referring to the Infragistics Help, the recommended one is the Infragistics.Windows.Editors.XamComboEditor
See the About xamComboEditor:
We recommend that you use the xamComboEditor control instead of the xamComboEditor (Input) control. The xamComboEditor (Input) is being planned for retirement over the next few years and will not receive any new features.
And now to your question:
Both, the Infragistics.Windows.Editors.XamComboEditor and the derived Infragistics.Windows.Ribbon.ComboEditorTool, have a property for setting a default text for null values. This property is called:
NullText
The text to display when the value of the editor is null and the editor is not in edit mode. The default value is empty string. (Inherited from Infragistics.Windows.Editors.TextEditorBase)
Also the Infragistics.Controls.Editors.XamComboEditor provides such a property. It is called:
EmptyText
Gets/Sets the Text that should be displayed when the editor doesn't have anything selected. (Inherited from Infragistics.Controls.Editors.ComboEditorBase)
Example for ComboEditorTool:
Referencing the following dlls:
InfragisticsWPF4.Editors.v18.1
InfragisticsWPF4.Ribbon.v18.1
InfragisticsWPF4.v18.1
xaml-Snippet:
xmlns:ribbon="http://infragistics.com/Ribbon"
...
<ribbon:ComboEditorTool Id="SampleComboEditorTool"
NullText="Select ..."
ItemsSource="{Binding }"
/>
Screenshot:
Example for Infragistics.Windows.Editors.XamComboEditor:
Referencing the following dlls:
InfragisticsWPF4.Editors.v18.1
InfragisticsWPF4.v18.1
xaml-Snippet:
xmlns:editors="http://infragistics.com/Editors"
...
<editors:XamComboEditor Width="120" Height="23"
ItemsSource="{Binding}"
NullText="Select ..."
/>
Screenshot:
Example for Infragistics.Controls.Editors.XamComboEditor:
Referencing the following dlls:
InfragisticsWPF4.Controls.Editors.XamComboEditor.v18.1
InfragisticsWPF4.v18.1
xaml-Snippet:
xmlns:ig="http://schemas.infragistics.com/xaml"
...
<ig:XamComboEditor Width="120" Height="23"
ItemsSource="{Binding}"
EmptyText="Select ..."
/>
Screenshot:
Second side note: the first occurrence I've found for the property NullText was in the Help Doc of Version 2012.1. See here