Change style of default text of Combo Box in XAML - c#

I'm trying to use XAML to make a combo box that is a drop-down list, that has default text already in the box in Italics, and when you click on the drop down to expand the list of options, all of the options will be listed in normal text rather than Italics. When a selection is made, I want the selected option to still be normal rather than Italic, even when it is in the same place as the default text. I'm new to XAML, and I am not sure how to do this, or if it is even possible?
My combo box is for now, as follows, where the default text to be shown is in the property 'Text'. Basically, I want 'Default Text' to be italic, but nothing else.
<ComboBox x:Name="ColumnComboBox" Grid.Column="1" Width="200" Margin="0,2" IsEditable="True" Text="Default Text" FontWeight="Normal" />
Any help is much appreciated.

You need to do a bit more of work to achieve this.
try this
<Window x:Class="WpfApplication1.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" Loaded="Window_Loaded">
<Grid>
<ComboBox Name="comboBox1" Margin="40,55,192,225" FontStyle="Italic">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding}" ></Label>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Label Name="lbl" Content="{Binding}" ></Label>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="lbl" Property="FontStyle" Value="Normal"> </Setter>
<Setter TargetName="lbl" Property="Background" Value="AliceBlue"></Setter>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="lbl" Property="FontStyle" Value="Italic"></Setter>
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="lbl" Property="FontStyle" Value="Normal"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</Grid>
</Window>
and here for testing the style from code behind
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<string> observableCollection = new ObservableCollection<string>();
public MainWindow()
{
for (int i = 0; i < 10; i++)
{
observableCollection.Add( "item:"+ i.ToString());
}
InitializeComponent();
comboBox1.ItemsSource = observableCollection;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
}
}

It is possible, but as you already noticed, default Text and the text shown when an item selected is the same text. That's why it will be tricky to set default text's style different from selected item's text style. The simplest implementation is to listen to ComboBox's selection changed event. When an item selected, change ComboBox's FontStyle to Normal, and when no item selected change it to Italic.
<ComboBox x:Name="ColumnComboBox" SelectionChanged="ColumnComboBox_SelectionChanged" IsEditable="True" Text="Default Text" FontWeight="Italic">
<ComboBoxItem Content="Item 1" FontStyle="Normal"/>
</ComboBox>
private void ColumnComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ColumnComboBox.FontStyle = ColumnComboBox.SelectedItem != null ? FontStyles.Normal : FontStyles.Italic;
}
Or maybe you actually want a watermark behavior for ComboBox. Check this blog post on decent implementation of watermarked ComboBox, downloadable source code available here.

Related

How to hide tab control when item source list is empty

I have a WPF .NET Core application with a TabControl bound to an ObservableCollection for the TabItems. I would like the TabControl to be hidden when the ObservableCollection becomes empty, and I would like to display another panel in its place. Then, when an item is added back to the ObservableCollection, I want the alternate panel hidden and the TabControl reshown. How would I accomplish this, hopefully in XAML with as little code-behind as possible? I know I can do it in code-behind.
Below is the key section of the app. I have hidden the TabControl, and included a Border control to represent the Panel that I will show when the TabControl is hidden.
<Window x:Class="TabTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TabTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Border Name="emptyTabPanel" Grid.Row="1" BorderBrush="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"
BorderThickness="1,1,1,1" Margin="5,0,5,5" Visibility="Hidden">
</Border>
<TabControl Name="MainTabControl" Visibility="Visible">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Header}" MinWidth="60"/>
<Button BorderThickness="0" Background="Transparent" Height="16" Width="15" Margin="15,2,0,0">
<Image Source="images/close.png" Height="8"/>
</Button>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</Grid>
</Window>
using System.Windows;
using System.Collections.ObjectModel;
namespace TabTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<TabEntry> tabEntries;
public MainWindow()
{
InitializeComponent();
tabEntries = new ObservableCollection<TabEntry>();
MainTabControl.ItemsSource = tabEntries;
for (int i = 1; i <= 5; i++)
{
tabEntries.Add(new TabEntry { Header = "tab " + i });
}
}
}
public class TabEntry
{
public string Header { get; set; }
}
}
All ItemsControls provide a HasItems property that you can use in a Trigger. In contrast to a DataTrigger on ItemsSource.Count this also works when ItemsSource is not set at all.
<TabControl ...>
<TabControl.Style>
<Style TargetType="TabControl">
<Style.Triggers>
<Trigger Property="HasItems" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</TabControl.Style>
...
</TabControl>
You can do it with a DataTriger in a Style. Note that you need to remove Visibility="Visible" or the Setter won't be able to change it.
<TabControl Name="MainTabControl" Background="Red">
<TabControl.Style>
<Style TargetType="TabControl">
<Style.Triggers>
<DataTrigger Binding="{Binding ItemsSource.Count, RelativeSource={RelativeSource Self}}" Value="0">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Style>
<TabControl.ItemTemplate>
<!-- and so on -->
You need a proper view model for this, rather than binding the tabs directly to the collection. That view model would include a HasItems property which you'll bind your TabControl visibility to, and an inverse property - say IsEmpty - which you'll bind the panel's visibility to.
Bind ObservableCollection's events to listen to changes in item count and raise PropertyChanged events for your view model appropriately.

WPF / XAML / MVVM - Set state of checkboxes based on conditions

I am a traditional MVC programmer that just started using MVVM and I do not know how I would program the below scenario the MVVM-way. I probably need multi binding, but can someone please help me and write that code for me? I've spend hours trying to achieve this, but I just don't know how to do it...
Btw, I know how to set the values from my settings file in XAML, but don't know how to write the other logic, EG:
IsEnabled="{Binding Source={x:Static p:Settings.Default}, Path=Pref_QuickProcess}"
This is my scenario:
I have a simple preferences screen with two checkboxes:
□ Quick process (value is set from Settings.Default.Pref_QuickProcess)
□ Upload to youtube (value is set from Settings.Default.Pref_UploadToYoutube)
The following conditions apply:
If "Quick process" is true, "Upload to youtube" should always be set to false and must be disabled.
If "Quick process" is false, "Upload to youtube" should be enabled.
These are the only options:
This is my XAML:
<Window x:Class="SchismRecorder.PreferencesWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="Preferences" Height="450" Width="800">
<Grid>
<GroupBox Header="Debug settings" HorizontalAlignment="Left" Height="326" Margin="21,20,0,0" VerticalAlignment="Top" Width="733">
<StackPanel>
<CheckBox Content="Quick process" HorizontalAlignment="Left" x:Name="chkQuickProcess" />
<CheckBox Content="Upload to Youtube" HorizontalAlignment="Left" x:Name="chkUploadToYoutube" />
</StackPanel>
</GroupBox>
</Grid>
This is my code behind:
public partial class PreferencesWindow : Window
{
public PreferencesWindow()
{
InitializeComponent();
chkQuickProcess.IsChecked = Settings.Default.Pref_QuickProcess;
chkUploadToYoutube.IsChecked = Settings.Default.Pref_UploadToYoutube;
ConfigureCheckboxes();
chkQuickProcess.Click -= ChkQuickProcess_Click;
chkQuickProcess.Click += ChkQuickProcess_Click;
}
private void ChkQuickProcess_Click(object sender, RoutedEventArgs e)
{
ConfigureCheckboxes();
}
void ConfigureCheckboxes()
{
if (chkQuickProcess.IsChecked.HasValue)
{
var isChecked = chkQuickProcess.IsChecked.Value;
if (isChecked)
{
chkUploadToYoutube.IsChecked = false;
chkUploadToYoutube.IsEnabled = false;
}
else
{
chkUploadToYoutube.IsEnabled = true;
}
}
}
protected override void OnClosing(CancelEventArgs e)
{
Settings.Default.Pref_QuickProcess = chkQuickProcess.IsChecked ?? false;
Settings.Default.Pref_UploadToYoutube = chkUploadToYoutube.IsChecked ?? false;
Settings.Default.Save();
base.OnClosing(e);
}
}
How do I get rid of my code behind, and get the same result in XAML with things like data triggers, converters, multi binding?
Edit: I think I do not necessarily need a viewmodel with setters to implement this logic, and do it with data triggers ? / multi binding ? instead. But maybe that is not possible?
You probably don't need a view model just to set a few properties in the Settings class that have a certain interdependence. The following XAML should do most or perhaps all of what you are describing.
When the first Checkbox is checked, the IsChecked and IsEnabled properties of the second Checkbox are set to false. However, the Settings.Default.Pref_UploadToYoutube property value is not changed. Not sure if this is strictly required.
By default, the second CheckBox's IsChecked property is bound to Pref_UploadToYoutube via a Style Setter. A DataTrigger on the Pref_QuickProcess property replaces the Binding and sets IsChecked and IsEnabled to false.
Also note the new Binding Path syntax for binding to static properties.
<CheckBox Content="Quick process"
IsChecked="{Binding Path=(p:Settings.Default).Pref_QuickProcess}"/>
<CheckBox Content="Upload to Youtube">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="IsChecked"
Value="{Binding Path=(p:Settings.Default).Pref_UploadToYoutube}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=(p:Settings.Default).Pref_QuickProcess}"
Value="True">
<Setter Property="IsChecked" Value="False"/>
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
You may also simplify the Settings properties binding paths by assigning the Settings.Default instance once to the DataContext of the StackPanel parent of the CheckBoxes:
<StackPanel DataContext="{Binding Path=(p:Settings.Default)}">
<CheckBox Content="Quick process" IsChecked="{Binding Pref_QuickProcess}"/>
<CheckBox Content="Upload to Youtube">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="IsChecked" Value="{Binding Pref_UploadToYoutube}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Pref_QuickProcess}" Value="True">
<Setter Property="IsChecked" Value="False"/>
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</StackPanel>

Colour multiple textboxes with one method wpf

I have a method that is supposed to color the background of a textbox that is on focus(Enter)
private void onEnter_ColourChange(object sender, EventArgs e)
{
this.BackColor = Color.White;
}
This doesn't work, however. I checked this answer, but I just got a NullReferenceException.
Essentially, I would like to color multiple textboxes when the textbox is in focus, with one method. Is there a way to do this?
First of all: TextBox has only the property Background and not BackColor!
If you want to change the color of the focused TextBox, a simple way is, to use a Style with a Trigger.
<Window.Resources>
<Style TargetType="TextBox" x:Key="TextBoxFocusBackgroundStyle" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true" >
<Setter Property="Background" Value="Brown" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
As you can see, the Trigger watches on the property IsFocused. If the TextBox got the Focus (so IsFocused changes to true), the Background will be brown.
To use this Style:
<StackPanel>
<TextBox Text="Peter" Style="{StaticResource TextBoxFocusBackgroundStyle}"/>
<TextBox Text="Laszlo" Style="{StaticResource TextBoxFocusBackgroundStyle}"/>
<TextBox Text="Julia" Style="{StaticResource TextBoxFocusBackgroundStyle}"/>
<TextBox Text="Romeo" Style="{StaticResource TextBoxFocusBackgroundStyle}"/>
</StackPanel>
If you already has a Style for the TextBoxes you should use that Style in the BasedOn attribute.

Change SelectedItem of ListBox from ViewModel

How can I get the SelectedItem of a ListBox to be highlighted after setting the SelectedItem in the ViewModel?
The ItemsSource is bound to an ObservableCollection of Bar (the collection is a member of a class Foo. A button is bound to a command that adds a new empty Bar instance to the collection and then also sets SelectedItem to the new empty instance.
After adding the instance to the collection, the ListBox is updated to show the new blank Bar. However, after setting the SelectedItem property in the ViewModel, the new instance is not highlighted in the ListBox but it is being set and the PropertyChanged event is raised (the SelectedItem is displayed elsewhere in the View).
Additional Details:
INotifyPropertyChanged is implemented in a base ViewModel class, and also implemented in the Foo and Bar classes.
The ListBox contains a custom ItemTemplate to display Bar members, and a custom ItemContainerStyle that modifies the Background for the IsMouseOver trigger.
simplified xaml:
<ListBox ItemsSource="{Binding Path=MyFoo.BarCollection}"
SelectedItem="{Binding Path=SelectedItem,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<Button Content="Add New Bar"
Command="{Binding Path=AddBarCommand}"/>
simplified viewmodel:
private Foo _myFoo;
public Foo MyFoo
{
get { return _myFoo; }
set { _myFoo= value; OnPropertyChanged("MyFoo"); }
}
private Bar _selectedItem;
public Bar SelectedItem
{
get { return _selectedItem; }
set { _selectedItem = value; OnPropertyChanged("SelectedItem"); }
}
private void AddBar()
{
Bar newBar = new Bar();
MyFoo.BarCollection.Add(newBar);
SelectedItem = newBar ;
_unsavedChanges = true;
}
Your code worked perfectly for me.
Notice that "Bar 3" is selected, but when the listbox doesn't have focus, the default theme on Windows 7 works pretty hard to keep you from noticing. And Windows 7 isn't even the worst of the bunch. Our application uses WhiteSmoke as a default background, and we've had major issues with older users1 being unable to tell if listbox items are selected or not.
Fortunately, it's a trivial fix. These resources could just as easily be in Window.Resources, in a global ListBox style, or in App.xaml. It's up to you how widely you want to apply them.
<ListBox
ItemsSource="{Binding MyFoo.BarCollection}"
SelectedItem="{Binding SelectedItem}"
>
<ListBox.Resources>
<SolidColorBrush
x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}"
Color="{x:Static SystemColors.HighlightColor}"
Opacity="0.5"
/>
<SolidColorBrush
x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}"
Color="{x:Static SystemColors.HighlightTextColor}"
/>
</ListBox.Resources>
</ListBox>
1 "Older" meaning old enough to vote.
And if your version of .NET predates the existence of SystemColors.InactiveSelectionHighlightTextBrushKey, this could probably use some refinement, but it works:
<ListBox
>
<ListBox.ItemContainerStyle>
<Style
TargetType="ListBoxItem"
BasedOn="{StaticResource {x:Type ListBoxItem}}"
>
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid
Background="{TemplateBinding Background}"
>
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter
Property="Background"
Value="{StaticResource {x:Static SystemColors.HighlightBrushKey}}"
/>
<Setter
Property="Foreground"
Value="{StaticResource {x:Static SystemColors.HighlightTextBrushKey}}"
/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True" />
<Condition Property="IsEnabled" Value="False" />
</MultiTrigger.Conditions>
<Setter
Property="Background"
Value="{StaticResource {x:Static SystemColors.InactiveCaptionBrushKey}}"
/>
</MultiTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

Can I check if the selectedItem of comboBox is there in the itemsource or not using triggers and/or converters?

I have various comboboxes in various view which have different itemsource so I want a generic way to check if the user selected value is there in the corresponding itemsource. As the comboboxes are editable so I want to inform user when He is enetering a junk value like by changing Foreground to Red.
<Window x:Class="CustomComboBox.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">
<Grid>
<StackPanel VerticalAlignment="Center">
<ComboBox Name="cmbBox" Width="100" Height="30" IsEditable="True" StaysOpenOnEdit="True" >
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<Trigger Property="SelectedIndex" Value="-1">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</StackPanel>
</Grid>
</Window>
Hope this helps

Categories

Resources