Here is what happens:
I have a listbox with items. Listbox has focus. Some item (say, 5th) is selected (has a blue background), but has no 'border'.
When I press 'Down' key, the focus moves from ListBox to the first ListBoxItem.
(What I want is to make 6th item selected, regardless of the 'border')
When I navigate using 'Tab', the Listbox never receives the focus again.
But when the collection is emptied and filled again, ListBox itself gets focus, pressing 'Down' moves the focus to the item.
How to prevent ListBox from gaining focus?
P.S.
listBox1.SelectedItem is my own class, I don't know how to make ListBoxItem out of it to .Focus() it.
EDIT: the code
Xaml:
<UserControl.Resources>
<me:BooleanToVisibilityConverter x:Key="visibilityConverter"/>
<me:BooleanToItalicsConverter x:Key="italicsConverter"/>
</UserControl.Resources>
<ListBox x:Name="lbItems">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<ProgressBar HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Visibility="{Binding Path=ShowProgress, Converter={StaticResource visibilityConverter}}"
Maximum="1"
Margin="4,0,0,0"
Value="{Binding Progress}"
/>
<TextBlock Text="{Binding Path=VisualName}"
FontStyle="{Binding Path=IsFinished, Converter={StaticResource italicsConverter}}"
Margin="4"
/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<me:OuterItem Name="Regular Folder" IsFinished="True" Exists="True" IsFolder="True"/>
<me:OuterItem Name="Regular Item" IsFinished="True" Exists="True"/>
<me:OuterItem Name="Yet to be created" IsFinished="False" Exists="False"/>
<me:OuterItem Name="Just created" IsFinished="False" Exists="True"/>
<me:OuterItem Name="In progress" IsFinished="False" Exists="True" Progress="0.7"/>
</ListBox>
where OuterItem is:
public class OuterItem : IOuterItem
{
public Guid Id { get; set; }
public string Name { get; set; }
public bool IsFolder { get; set; }
public bool IsFinished { get; set; }
public bool Exists { get; set; }
public double Progress { get; set; }
/// Code below is of lesser importance, but anyway
///
#region Visualization helper properties
public bool ShowProgress
{
get
{
return !IsFinished && Exists;
}
}
public string VisualName
{
get
{
return IsFolder ? "[ " + Name + " ]" : Name;
}
}
#endregion
public override string ToString()
{
if (IsFinished)
return Name;
if (!Exists)
return " ??? " + Name;
return Progress.ToString("0.000 ") + Name;
}
public static OuterItem Get(IOuterItem item)
{
return new OuterItem()
{
Id = item.Id,
Name = item.Name,
IsFolder = item.IsFolder,
IsFinished = item.IsFinished,
Exists = item.Exists,
Progress = item.Progress
};
}
}
Сonverters are:
/// Are of lesser importance too (for understanding), but will be useful if you copy-paste to get it working
public class BooleanToItalicsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool normal = (bool)value;
return normal ? FontStyles.Normal : FontStyles.Italic;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool exists = (bool)value;
return exists ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
But most important, is that UserControl.Loaded() has:
lbItems.Items.Clear();
lbItems.ItemsSource = fsItems;
where fsItems is ObservableCollection<OuterItem>.
The usability problem I describe takes place when I Clear() that collection (fsItems) and fill with new items.
Please provide your code. Usually the cause of this problem lies in ContentPresenters and KeyboardNavigation.IsTabStop property. But sometimes it's not. So the code would help.
The answer to your question may depend on the way your listbox is getting focus. Here is the solution if you are using an access key (ex: alt+c). You have to implement your own listbox control and override the OnAccessKey method. If this is not your scenario, then I would suggest looking into the OnIsKeyboardFocusWithinChanged method. Try using the same approach I did in the code below.
protected override void OnAccessKey(System.Windows.Input.AccessKeyEventArgs e)
{
if (SelectedIndex >= 0)
{
UIElement element = ItemContainerGenerator.ContainerFromIndex(SelectedIndex) as UIElement;
if (element != null)
{
element.Focus();
}
}
}
Related
I would like to display "Yes" or "No" for whenever a Boolean type data received(it can receive different types of data) for generating a column in RadGridView instead of a checkbox. I would like to implement this changes in xaml. Columns are generating dynamically. This is how it's created now:
<telerik:RadGridView x:Name="Data" Grid.Row="3" Margin="5" AutoGenerateColumns="False" CanUserSortColumns="True" IsFilteringAllowed="True"
grid:RadGridViewColumnsBinding.ColumnsCollection="{Binding Path=ColumnsData}"
IsReadOnly="False" CanUserResizeColumns="True"/>
I am new in Silverlight coding. Will really appreciate if someone can help.
You should check out Telerik's ConditionalDataTemplateSelector they have in this demo, and read about IValueConverter if you haven't already.
Depending on what you are trying to do with all your columns, the ConditionalDataTemplateSelector might be overkill, but you can use it to create a rule system for what DataTemplate to use for a given cell based on a custom rule system.
<Grid.Resources>
...
<DataTemplate x:Key="CellDisplayTextBox">
<TextBlock Text="{Binding Value, Converter={StaticResource BooleanToYesNoConverter}}" />
</DataTemplate>
<selector:ConditionalDataTemplateSelector x:Key="displaySelector" ConditionConverter="{StaticResource someConverter}">
<selector:ConditionalDataTemplateSelector.Rules>
<selector:ConditionalDataTemplateRule DataTemplate="{StaticResource CellDisplayTextBox}">
<selector:ConditionalDataTemplateRule.Value>
<sys:Int32>1</sys:Int32> <!--You need to figure out what value and type to use here -->
</selector:ConditionalDataTemplateRule.Value>
</selector:ConditionalDataTemplateRule>
...
</selector:ConditionalDataTemplateSelector.Rules>
</Grid.Resources>
...
<telerikGridView:RadGridView>
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn CellTemplateSelector="{StaticResource displaySelector}" CellEditTemplateSelector="{StaticResource editSelector}" />
</telerik:RadGridView.Columns>
</telerikGridView:RadGridView>
The IValueConverter will let you bind a bool value, but display a string value. For a BooleanToYesNoConverter you could do something like:
public class BooleanToYesNoConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool? bValue = value as bool?;
if (bValue.HasValue)
return bValue.Value ? "Yes" : "No";
else
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string sValue = value as string;
return sValue == "Yes";
}
}
The ConditionalDataTemplateSelector code from the demo:
public class ConditionalDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
object conditionValue = this.ConditionConverter.Convert(item, null, null, null);
foreach (ConditionalDataTemplateRule rule in this.Rules)
{
if (Equals(rule.Value, conditionValue))
{
return rule.DataTemplate;
}
}
return base.SelectTemplate(item, container);
}
List<ConditionalDataTemplateRule> _Rules;
public List<ConditionalDataTemplateRule> Rules
{
get
{
if (this._Rules == null)
{
this._Rules = new List<ConditionalDataTemplateRule>();
}
return this._Rules;
}
}
IValueConverter _ConditionConverter;
public IValueConverter ConditionConverter
{
get
{
return this._ConditionConverter;
}
set
{
this._ConditionConverter = value;
}
}
}
public class ConditionalDataTemplateRule
{
object _Value;
public object Value
{
get
{
return this._Value;
}
set
{
this._Value = value;
}
}
DataTemplate _DataTemplate;
public DataTemplate DataTemplate
{
get
{
return this._DataTemplate;
}
set
{
this._DataTemplate = value;
}
}
}
In my application I have 3 data grids in a single xaml file. Based on the User selection I want show one grid and hide other grids.
in my view model class I have Boolean property for each grid and based on the selection I am setting it to true or false.But all grids are visible .
<DataGrid Visibility="{Binding Path=IsGridVisible}" >
In my view model I am setting IsGridVisible value
public bool IsCapexGridVisible
{
get { return isCapexGridVisible; }
set { isCapexGridVisible = value; RaisePropertyChangedEvent("IsCapexGridVisible"); }
}
Please provide your ideas. Thanks
There is a BooleanToVisibilityConverter available to you that converts true to System.Windows.Visibility.Visible and false to System.Windows.Visibility.Collapsed.
So you can take help of this pre built converter and must add it to resources.
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
Create a property of type bool in your ViewModel
bool _dgVisibility;
public bool DataGridVisibility
{
get { return _dgVisibility; }
set
{
_dgVisibility = value;
OnPropertyChanged("DataGridVisibility");
}
}
and you can use it as below
<DataGrid Visibility="{Binding Path=DataGridVisibility, Converter={StaticResource BoolToVis}}"/>
Visibility property on UIElement is not a boolean. It is an enum with three values:
Collapsed Do not display the element, and do not reserve space for it in layout.
Hidden Do not display the element, but reserve space for the element in layout.
Visible Display the element.
So in order to set it properly from ViewModel you should:
- make your property type of Visibility (not best solution in the world)
- Use converter for the binding which will do the trick of translating boolean to visibility
public class BooleanToCollapsedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType == typeof(Visibility) && value is bool)
{
return (bool)value ? Visibility.Visible : Visibility.Collapsed;
}
throw new FormatException();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Additional converter variant with visibility customization
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
[MarkupExtensionReturnType(typeof(IValueConverter))]
public class BoolToVisibilityConverter : MarkupExtension, IValueConverter
{
[ConstructorArgument("TrueValue")]
public Visibility TrueValue { get; set; }
[ConstructorArgument("FalseValue")]
public Visibility FalseValue { get; set; }
[ConstructorArgument("NullValue")]
public Visibility NullValue { get; set; }
public BoolToVisibilityConverter()
{
TrueValue = Visibility.Visible;
FalseValue = Visibility.Collapsed;
NullValue = Visibility.Collapsed;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return NullValue;
if (value is not bool boolValue)
return null;
return boolValue ? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (Equals(value, TrueValue))
return true;
if (Equals(value, FalseValue))
return false;
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
Usage:
<someControl ...
xmlns:converters="clr-namespace:ExampleNamespace.Converters;assembly=ExampleAssembly"
...
>
...
Visibility="{Binding IsSearchInProgress,
Mode=OneWay,
Converter={converters:BoolToVisibilityConverter}}"
Visibility="{Binding IsSearchInProgress,
Mode=OneWay,
Converter={converters:BoolToVisibilityConverter TrueValue=Collapsed, FalseValue=Visible}}"
First of all, I am new to WPF and Xaml, so I just hope that you understand what I am asking.
I got this situation: There is a listBox of Animals. Every Animal has Weight property. What I am trying to achieve is whenever Weight of Animal is greater then 300 kg, that Weight should be displayed red.
You could use custom converter to achieve that. If your item looks like that:
public class Animal
{
public int Weight { get; set; }
public string Name { get; set; }
}
and ItemTemplate like that:
<DataTemplate x:Key="AnimalTemplate">
<TextBlock Text="{Binding Name}" Foreground="{Binding Weight, Converter={StaticResource AnimalColorSelector}}"/>
</DataTemplate>
Your converter will be like the following one:
public class AnimalColorSelector : IValueConverter
{
private readonly Color _overweightColor = Colors.Red;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is int)
{
return (int) value > 300 ? new SolidColorBrush(_overweightColor) : Binding.DoNothing;
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
This approach has the following pros:
You don't need to hardcode the default color, but inherit it by using Binding.DoNothing.
You don't need to store any style information in a view model.
You could create a ViewModel for Animals that would contain necessary logic for color setting. Like this:
public class VMAnimal : INotifyPropertyChanged
{
private int _weight;
public int Weight
{
get { return _weight; }
set
{
_weight = value;
RaisePropertyChanged("Weight");
RaisePropertyChanged("Color");
}
}
public Brush Foreground
{
get
{
if (Weight > 300)
return new SolidColorBrush(Color.Red);
return new SolidColorBrush(Color.Black);
}
}
}
And use it with binding like this:
<TextBlock Text="{Binding Weight}" Foreground="{Binding Foreground}" />
I have a textblock inside a list view that I need to hide or collapse when it is empty or null. I tried using a string converter but that does not do it.
Any other ideas?
Update # 1:
Here is the code inside the textblock:
Visibility="{Binding Converter={StaticResource StringConverter}}
Here is the converter:
public class StringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
return string.IsNullOrEmpty(value.ToString()) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
I would recommend creating text and visibility bindings on your textbox.
Here's an example of the view model properties you'd have.
public String TextBoxText
{
get { return textBoxText; }
set
{
if (value != textBoxText)
{
textBoxText= value;
SetTextBoxVisibility();
OnPropertyChanged("TextBoxText");
}
}
}
private String textBoxText;
public Visibility TextBoxVisibility
{
get { return textBoxVisibility; }
set
{
if (value != textBoxVisibility)
{
textBoxVisibility= value;
OnPropertyChanged("TextBoxVisibility");
}
}
}
private Visibility textBoxVisibility;
public void SetTextBoxVisibility()
{
this.TextBoxVisibility = String.IsNullOrEmpty(this.TextBoxText) ? Visibility.Collapsed : Visibility.Visible;
}
The only thing you've not shown of your code is where you instantiate the converter class. Is this because you're not doing so?
Typically you'd add something like this to app.xaml:
<Application.Resources>
<ResourceDictionary>
<conv:StringConverter x:Key="StringConverter " />
</ResourceDictionary>
</Application.Resources>
When I select (by clicking or by keyboard) blank row on my DataGrid (when I want to add new row), unexpected validation error occurs (but with no exception) - the border of datagrid changes to red color, as you can see on the image below. When I click second time on blank row, the red border dissapears. Everything other works fine, the new row is added. Besides, I don't have any validation rules. And when I make a row with empty text, value is valid.
I don't want this behavior and this red border, anybody knows, why this happens and how to fix it? Why and where some validation fails?
Below I append some source code:
DataGrid definition in xaml:
<DataGrid IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"
ItemsSource="{Binding Path=ConfigFiles}" SelectedItem="{Binding Path=SelectedConfigFile}"
Grid.Column="1" Height="87" Margin="0,26,11,32" Style="{DynamicResource DataGridStyle}">
<DataGrid.Columns>
<DataGridTextColumn Width="1*" Binding="{Binding Name}" />
</DataGrid.Columns>
</DataGrid>
My ViewModel's part:
public class ManageModulesVM : BaseVM // Implements INotifyPropertyChanged
{
// ...
public ObservableCollection<ConfigFile> ConfigFiles
{
get { return selectedModule == null ? null : selectedModule.ConfigFiles; }
set
{
selectedModule.ConfigFiles = value;
OnPropertyChanged(() => ConfigFiles);
}
}
public ConfigFile SelectedConfigFile
{
get { return selectedModule == null ? null : selectedModule.SelectedConfigFile; }
set
{
if (value != null)
{
selectedModule.SelectedConfigFile = value;
}
OnPropertyChanged(() => SelectedConfigFile);
OnPropertyChanged(() => Parameters);
}
}
// ...
}
ConfigFile class:
public class ConfigFile
{
public string Name { get; set; }
public IList<Parameter> Parameters { get; set; }
public ConfigFile() { Name = ""; Parameters = new List<Parameter>(); }
}
Edit:
After further investigation I know, that SelectedItem Binding is causing problems (when I remove this binding, validation error stops to appear), but I still don't know why and how to fix this.
I've found my own solution to this question. I've written a value converter and tied it to the binding:
(SelectedItem="{Binding Path=SelectedConfigFile,Converter={StaticResource configFileConverter}}")
The converter class:
namespace Converters
{
public class SelectedConfigFileConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if(value is ConfigFile)
return value;
return null;
}
}
}
Define resource in resources.xaml file (or in any other resources place):
<ResourceDictionary (...) xmlns:conv="clr-namespace:Converters" >
<conv:SelectedConfigFileConverter x:Key="configFileConverter" />
</ResourceDictionary>
The advantage of this solution is that the SelectedConfigFile property's type did't changed (to the general object type) so it is still strongly typed.
To get the reason, when you click the new row of DataGrid in Debug mode, please see the debug window. There are first exception messages which will give you the idea why your problem is occurred.
Yes, the problem is from type casting. You need to modify the type of SelectedItem to object type as below.
public class ManageModulesVM : BaseVM // Implements INotifyPropertyChanged
{
// ...
public object SelectedConfigFile
{
get { return selectedModule == null ? null : selectedModule.SelectedConfigFile; }
set
{
if (value != null)
{
selectedModule.SelectedConfigFile = value;
}
OnPropertyChanged(() => SelectedConfigFile);
OnPropertyChanged(() => Parameters);
}
}
// ...
}
Here's a general-purpose converter you can use for any DataGrid, binding any kind of item:
public class DataGridItemConverter : MarkupExtension, IValueConverter
{
static DataGridItemConverter converter;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value != null && value.GetType() == targetType) ? value : null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (converter == null)
converter = new DataGridItemConverter();
return converter;
}
}
Since it implements MarkupExtension you don't even need to define a static resource, you can just reference it like this:
SelectedItem="{Binding SelectedThing,Converter={conv:DataGridItemConverter}}"
You can just add this line to your DataGrid:
<DataGrid Validation.ErrorTemplate="{x:Null}" />
You can just add this line to your DataGrid:
<DataGrid Validation.ErrorTemplate="{x:Null}" />
It will solve the problem