I'm new to WPF and have a obviously very simple or basic problem which I failed to solve even though I have read for hours to find a solution. Here it is:
I have two Comboboxes, a master and a detail combobox. The master has two entries: "Option 1" and "Option 2". The detail combobox should have two entries "Value 1" and "Value 2" when "Option 1" is selected or "Value A" and "Value B" when "Option 2" is selected in the master combobox.
I think that should be possible with pure XAML.
Basically I tried to assign a dictionary collection to each of the comboboxes and tried to reference (bind) the master list via the "Key".
Following is my xaml-code so far (I have withdrawn all my bind-attempts)
<Window x:Class="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:Test"
mc:Ignorable="d"
xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
Title="MainWindow" Height="221" Width="457">
<Window.Resources>
<col:ArrayList x:Key="masterlist">
<col:DictionaryEntry Key="option1" Value="Option one"/>
<col:DictionaryEntry Key="option2" Value="Option two" />
</col:ArrayList>
<col:ArrayList x:Key="detaillist">
<col:DictionaryEntry Key="option1" Value="Value 1"/>
<col:DictionaryEntry Key="option1" Value="Value 2"/>
<col:DictionaryEntry Key="option2" Value="Value A"/>
<col:DictionaryEntry Key="option2" Value="Value B"/>
</col:ArrayList>
</Window.Resources>
<Grid Margin="0,0,0,29">
<ComboBox x:Name="ComboboxMaster" HorizontalAlignment="Left" Margin="33,39,0,0" VerticalAlignment="Top" Width="120"
ItemsSource ="{StaticResource masterlist}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
IsSynchronizedWithCurrentItem="True"
SelectedIndex="0">
</ComboBox>
<ComboBox x:Name="ComboboxDetail" HorizontalAlignment="Center" Margin="0,39,0,0" VerticalAlignment="Top" Width="120"
ItemsSource ="{StaticResource detaillist}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedIndex="0">
</ComboBox>
<Label x:Name="labelMasterKey" HorizontalAlignment="Left" Margin="33,75,0,0" VerticalAlignment="Top" Width="120"
Content="{Binding ElementName=ComboboxMaster, Path=SelectedValue}"/>
<Button Content="Close" HorizontalAlignment="Left" Margin="33,123,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.071,-0.349" Click="Button_Click" Height="26" Width="64"/>
</Grid>
</Window>
Your help for a dummy like me is appreciated
Split the detail list into two lists and assign the appropriate one in the Setter of a DataTrigger on the SelectedValue of the master ComboBox:
<Window.Resources>
<col:ArrayList x:Key="masterlist">
<col:DictionaryEntry Key="option1" Value="Option one"/>
<col:DictionaryEntry Key="option2" Value="Option two" />
</col:ArrayList>
<col:ArrayList x:Key="detaillist1">
<col:DictionaryEntry Key="option1" Value="Value 1"/>
<col:DictionaryEntry Key="option2" Value="Value 2"/>
</col:ArrayList>
<col:ArrayList x:Key="detaillist2">
<col:DictionaryEntry Key="option1" Value="Value A"/>
<col:DictionaryEntry Key="option2" Value="Value B"/>
</col:ArrayList>
</Window.Resources>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<ComboBox x:Name="ComboboxMaster" Grid.Column="0" Width="120" Margin="20"
ItemsSource="{StaticResource masterlist}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedIndex="0"/>
<ComboBox x:Name="ComboboxDetail" Grid.Column="1" Width="120" Margin="20"
DisplayMemberPath="Value"
SelectedValuePath="Key">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedValue,
ElementName=ComboboxMaster}"
Value="option1">
<Setter Property="ItemsSource"
Value="{StaticResource detaillist1}"/>
<Setter Property="SelectedValue"
Value="option1"/>
</DataTrigger>
<DataTrigger Binding="{Binding SelectedValue,
ElementName=ComboboxMaster}"
Value="option2">
<Setter Property="ItemsSource"
Value="{StaticResource detaillist2}"/>
<Setter Property="SelectedValue"
Value="option1"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</StackPanel>
Another possibility would be using the MVVM pattern. THis brings you to more flexibility with your data and seperating the data from your view.
First you have to create a new ViewModel class like this:
public class MainWindowViewModel : INotifyPropertyChanged
{
private Dictionary<string, List<string>> _comboboxData;
private string _selectedItem1;
private string _selectedItem2;
public event PropertyChangedEventHandler PropertyChanged;
public MainWindowViewModel()
{
_comboboxData = new Dictionary<string, List<string>>
{
{"option1", new List<string>{"Value 1", "Value 2"}},
{"option2", new List<string>{"Value A", "Value B"}},
};
_selectedItem1 = "option1";
_selectedItem2 = _comboboxData[SelectedItem1][0];
}
public string SelectedItem1
{
get => _selectedItem1;
set
{
if (value == _selectedItem1) return;
_selectedItem1 = value;
RaisePropertyChanged();
RaisePropertyChanged(nameof(Combobox2Data));
SelectedItem2 = _comboboxData[SelectedItem1][0]
}
}
public string SelectedItem2
{
get => _selectedItem2;
set
{
if (value == _selectedItem2) return;
_selectedItem2 = value;
RaisePropertyChanged();
}
}
public ObservableCollection<string> Combobox1Data => new ObservableCollection<string>(_comboboxData.Keys);
public ObservableCollection<string> Combobox2Data =>
new ObservableCollection<string>(_comboboxData[_selectedItem1]);
private void RaisePropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
with this your WPF MainWindow.xaml will look like this:
<Window x:Class="WpfTest.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:WpfTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid Margin="0,0,0,29">
<ComboBox x:Name="ComboboxMaster" HorizontalAlignment="Left" Margin="33,39,0,0" VerticalAlignment="Top" Width="120"
ItemsSource ="{Binding Combobox1Data}"
SelectedItem="{Binding SelectedItem1}">
</ComboBox>
<ComboBox x:Name="ComboboxDetail" HorizontalAlignment="Center" Margin="0,39,0,0" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Combobox2Data}"
SelectedItem="{Binding SelectedItem2}">
</ComboBox>
<Label x:Name="labelMasterKey" HorizontalAlignment="Left" Margin="33,75,0,0" VerticalAlignment="Top" Width="120"
Content="{Binding SelectedItem2}"/>
<Button Content="Close" HorizontalAlignment="Left" Margin="33,123,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.071,-0.349" Height="26" Width="64"/>
</Grid>
</Window>
With this you may change your data dictionary with more values e.g. for ComboBox 1 (Keys) or more values for each keyed List (Values) only at beginning of ViewModel class without other changes anywhere
«Pure» XAML:
<Window.Resources>
<col:ArrayList x:Key="masterlist">
<col:DictionaryEntry Key="Option one">
<col:DictionaryEntry.Value>
<col:ArrayList>
<col:DictionaryEntry Key="option1" Value="Value 1"/>
<col:DictionaryEntry Key="option1" Value="Value 2"/>
</col:ArrayList>
</col:DictionaryEntry.Value>
</col:DictionaryEntry>
<col:DictionaryEntry Key="Option one">
<col:DictionaryEntry.Value>
<col:ArrayList>
<col:DictionaryEntry Key="option2" Value="Value A"/>
<col:DictionaryEntry Key="option2" Value="Value B"/>
</col:ArrayList>
</col:DictionaryEntry.Value>
</col:DictionaryEntry>
</col:ArrayList>
</Window.Resources>
<Grid Margin="0,0,0,29">
<ComboBox x:Name="ComboboxMaster" HorizontalAlignment="Left" Margin="33,39,0,0" VerticalAlignment="Top" Width="120"
ItemsSource ="{StaticResource masterlist}"
DisplayMemberPath="Key"
SelectedIndex="0">
</ComboBox>
<ComboBox x:Name="ComboboxDetail" HorizontalAlignment="Center" Margin="0,39,0,0" VerticalAlignment="Top" Width="120"
ItemsSource ="{Binding SelectedItem.Value, ElementName=ComboboxMaster}"
DisplayMemberPath="Value"
SelectedIndex="0">
</ComboBox>
<Label x:Name="labelMasterKey" HorizontalAlignment="Left" Margin="33,75,0,0" VerticalAlignment="Top" Width="120"
Content="{Binding ElementName=ComboboxDetail, Path=SelectedItem.Value}"/>
Related
I have this WPF application. Whenever I open MainWindow.xaml, the xaml file opens very slowly or when I run the solution when MainWindow.xaml is open, the solution runs very slowly. How can I fix this? Here is my MainWindow.xaml :
<Window x:Class="CodeFirstMVVM.App.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cm="clr-namespace:System.ComponentModel;assembly=System"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:CodeFirstMVVM.App.ViewModels"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
DataContext="{Binding Source={StaticResource Locator}, Path=OgrenciView}"
Title="MainWindow" Height="500" Width="900">
<Grid>
<Canvas>
<Button Content="Yeni" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Command="{Binding EkleCommand}"/>
<Button Content="Güncelle" HorizontalAlignment="Left" Margin="109,10,0,0" VerticalAlignment="Top" Width="75" Command="{Binding DuzenleCommand}"/>
<Button Content="Sil" HorizontalAlignment="Left" Margin="207,10,0,0" VerticalAlignment="Top" Width="75" Command="{Binding SilCommand}"/>
<Button Content="Kaydet" HorizontalAlignment="Left" Margin="777,15,0,0" VerticalAlignment="Top" Width="75" Command="{Binding KaydetCommand}"/>
<Label Content="AdSoyad" Canvas.Left="25" Canvas.Top="51"/>
<Label Content="OkulNo" Canvas.Left="25" Canvas.Top="82"/>
<Label Content="Sınıf" Canvas.Left="25" Canvas.Top="113" RenderTransformOrigin="0.053,-0.231"/>
<Label Content="Yaş" Canvas.Left="25" Canvas.Top="144" RenderTransformOrigin="-0.053,-0.077"/>
<TextBox Height="23" Canvas.Left="131" TextWrapping="Wrap" Canvas.Top="51" Width="283" Name="txtAdSoyad" Text="{Binding AdSoyadUI}"/>
<TextBox Height="23" Canvas.Left="131" TextWrapping="Wrap" Canvas.Top="86" Width="283" Name="txtOkulNo" Text="{Binding OkulNoUI}"/>
<TextBox Height="23" Canvas.Left="131" TextWrapping="Wrap" Canvas.Top="117" Width="283" Name="txtSinif" Text="{Binding SinifUI}"/>
<TextBox Height="23" Canvas.Left="131" TextWrapping="Wrap" Canvas.Top="148" Width="283" Name="txtYas" Text ="{Binding YasUI}"/>
<DataGrid x:Name="dg01" ItemsSource="{Binding Entities, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedEntity}" CanUserAddRows="False" Canvas.Left="25" Canvas.Top="201" AutoGenerateColumns="False" IsReadOnly="True" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding AdSoyad}" Header="AdSoyad" Width="200" />
<DataGridTextColumn Binding="{Binding OkulNo}" Header="OkulNo" Width="50" />
<DataGridTextColumn Binding="{Binding Sinif}" Header="Sınıf" Width="50" />
<DataGridTextColumn Binding="{Binding Yas}" Header="Yaş" Width="50" />
</DataGrid.Columns>
</DataGrid>
<DataGrid ItemsSource="{Binding SelectedEntity.Notlar}" Canvas.Left="629" Canvas.Top="201" AutoGenerateColumns="False" >
<!-- <DataGrid ItemsSource="{Binding SelectedEntity.Notlar}" Canvas.Left="622" Canvas.Top="201" AutoGenerateColumns="False" >-->
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding NotDeger}" Header="Notlar"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Canvas>
</Grid>
</Window>
Here is my App.xaml :
<Application x:Class="CodeFirstMVVM.App.App"
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"
StartupUri="MainWindow.xaml"
mc:Ignorable="d">
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:CodeFirstMVVM.ViewModels" />
</Application.Resources>
</Application>
I have a GetEntities method, adding a IsInDesignMode check seems to solve my problem :
protected override ObservableCollection<Student> GetEntities()
{
if (IsInDesignMode)
{
return null;
}
else
{
_entities = new ObservableCollection<Student>(_context.Set<Student>());
return _entities;
}
}
I have an observable collection of Song objects. These song objects have a property called "Playing" that is a bool (bad naming, I know). The songs display in a ListBox in my application. I want the song that is Playing to be colored red. I have been working with triggers all day trying to make this work. So far, I have gotten to the point where they are colored based on what Playing is set to when the song is added to the list. Is it possible to make it change when the Playing property changes?
Here is my XAML:
<Window x:Class="MusicPlayer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:MusicPlayer="clr-namespace:MusicPlayer" Title="Music" Height="350" Width="525" Name="Main">
<Grid>
<ListBox HorizontalAlignment="Stretch" Name="Playlist" VerticalAlignment="Stretch" Margin="0,23,0,79" MouseDoubleClick="Playlist_MouseDoubleClick">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Playing}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
<DataTemplate DataType="{x:Type MusicPlayer:Song}">
<TextBlock Text="{Binding Path=Title}"/>
</DataTemplate>
</ListBox.Resources>
</ListBox>
<Button Content="Play" Height="23" HorizontalAlignment="Center" Margin="0,0,330,20" Name="PlayButton" VerticalAlignment="Bottom" Width="75" Click="PlayButton_Click" />
<Button Content="Stop" Height="23" HorizontalAlignment="Center" Margin="0,0,165,20" Name="stopButton" VerticalAlignment="Bottom" Width="75" Click="stopButton_Click" />
<Button Content="Next" Height="23" HorizontalAlignment="Center" Margin="165,0,0,20" Name="nextButton" VerticalAlignment="Bottom" Width="75" Click="nextButton_Click" />
<Button Content="Add Songs..." Height="23" HorizontalAlignment="Center" Margin="330,0,0,20" Name="AddButton" VerticalAlignment="Bottom" Width="75" Click="AddButton_Click" />
<Button Content="Previous" Height="23" HorizontalAlignment="Center" Margin="0,0,0,20" Name="PreviousButton" VerticalAlignment="Bottom" Width="75" Click="PreviousButton_Click" />
<Button Content="Pause" Height="23" HorizontalAlignment="Center" Margin="0,0,330,20" Name="PauseButton" VerticalAlignment="Bottom" Width="75" Visibility="Hidden" Click="PauseButton_Click" />
<Menu Height="23" HorizontalAlignment="Stretch" Name="menu1" VerticalAlignment="Top">
<MenuItem Header="File">
<MenuItem Header="Quit" Click="MenuItem_Click" />
</MenuItem>
</Menu>
<Slider Height="23" HorizontalAlignment="Stretch" Margin="10,0,10,50" Name="ProgressBar" VerticalAlignment="Bottom" />
</Grid>
</Window>
Edit:
After implementign INotifyPropertyChanged, The color will change to red using either the above or below XAML. Now, it won't change back white using either method. I am also having a problem where my Play method will not change the background color to red, but my next method will. Here is my code: http://pastebin.com/EMTUpTin http://pastebin.com/LuK78zGp
Here is my new XAML:
<Window x:Class="MusicPlayer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:MusicPlayer="clr-namespace:MusicPlayer" Title="Music" Height="350" Width="525" Name="Main">
<Grid>
<ListBox HorizontalAlignment="Stretch" Name="Playlist" VerticalAlignment="Stretch" Margin="0,23,0,79" MouseDoubleClick="Playlist_MouseDoubleClick">
<ListBox.Resources>
<DataTemplate DataType="{x:Type MusicPlayer:Song}">
<TextBlock Text="{Binding Path=Title}">
<TextBlock.Background>
<SolidColorBrush Color="{Binding BackgroundColor}"/>
</TextBlock.Background>
</TextBlock>
</DataTemplate>
</ListBox.Resources>
</ListBox>
<Button Content="Play" Height="23" HorizontalAlignment="Center" Margin="0,0,330,20" Name="PlayButton" VerticalAlignment="Bottom" Width="75" Click="PlayButton_Click" />
<Button Content="Stop" Height="23" HorizontalAlignment="Center" Margin="0,0,165,20" Name="stopButton" VerticalAlignment="Bottom" Width="75" Click="stopButton_Click" />
<Button Content="Next" Height="23" HorizontalAlignment="Center" Margin="165,0,0,20" Name="nextButton" VerticalAlignment="Bottom" Width="75" Click="nextButton_Click" />
<Button Content="Add Songs..." Height="23" HorizontalAlignment="Center" Margin="330,0,0,20" Name="AddButton" VerticalAlignment="Bottom" Width="75" Click="AddButton_Click" />
<Button Content="Previous" Height="23" HorizontalAlignment="Center" Margin="0,0,0,20" Name="PreviousButton" VerticalAlignment="Bottom" Width="75" Click="PreviousButton_Click" />
<Button Content="Pause" Height="23" HorizontalAlignment="Center" Margin="0,0,330,20" Name="PauseButton" VerticalAlignment="Bottom" Width="75" Visibility="Hidden" Click="PauseButton_Click" />
<Menu Height="23" HorizontalAlignment="Stretch" Name="menu1" VerticalAlignment="Top">
<MenuItem Header="File">
<MenuItem Header="Quit" Click="MenuItem_Click" />
</MenuItem>
</Menu>
<Slider Height="23" HorizontalAlignment="Stretch" Margin="10,0,10,50" Name="ProgressBar" VerticalAlignment="Bottom" />
</Grid>
</Window>
Edit 2:
I just noticed I am having another problem. If I highlight one of the items with a white background, the text is invisible. How do I fix this?
Edit 3:
Fixed this problem by setting the background to Colors.Transparent instead of Colors.White.
In order to have it change back, wouldn't you also have to implement a trigger for when Playing is False? Like so:
<ListBox HorizontalAlignment="Stretch" Name="Playlist" VerticalAlignment="Stretch" Margin="0,23,0,79" MouseDoubleClick="Playlist_MouseDoubleClick">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Playing}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Playing}" Value="False">
<Setter Property="Background" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
<DataTemplate DataType="{x:Type MusicPlayer:Song}">
<TextBlock Text="{Binding Path=Title}"/>
</DataTemplate>
</ListBox.Resources>
This seems like the simplest solution to me. Of course you can change Value="White" to whatever color you need.
Does your object with the Playing property implement INotifyPropertyChanged ? If it does, then your UI should auto-update based on the DataTrigger approach you are using.
Another approach is to use ViewModels instead of Triggers (easier to understand and work with - when things don't go as expected) An example
Update:
Just looked at your code snippets. One thing I found - you need to trigger the event after you've applied the new value.
public Color BackgroundColor
{
get { return _backgroundColor; }
set
{
_backgroundColor = value;
NotifyPropertyChanged("BackgroundColor"); // must be after so that new value is readable by the GUI
}
}
Also the dataTemplate must be applied to the ItemTemplate property of the listbox
<ListBox.ItemTemplate> <!-- not Resources property -->
<DataTemplate DataType="{x:Type MusicPlayer:Song}">
I basically changed the snippet from the example that I posted to use your song class and then modified the button click to toggle between 2 colors. (Also turned ListView into ListBox)
private bool _isGreen = false;
private void Button_Click(object sender, RoutedEventArgs e)
{
foreach (var item in Items)
item.BackgroundColor = (_isGreen ? Colors.Cyan : Colors.PaleGreen );
_isGreen = !_isGreen;
}
My listboxes change color on every click! :)
I wrote a usercontrol of DataGrid with CellEditingTemplate. The DataTemplate of this editing-Template is a TextBox, and the cursor will go into the textbox by three times click, what can i do, if i want set the cursor on the textbox by double click or one click?
Here is my code:
<Window x:Class="MultiLineEditDataGrid.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MultiLineEditDataGrid"
Title="MainWindow" Height="350" Width="525">
<Grid DataContext="{Binding Source={x:Static Application.Current}, Path=CompanyManager}">
<Grid.RowDefinitions>
<RowDefinition Height="270"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding Companies}" CanUserAddRows="False" AutoGenerateColumns="False">
<DataGrid.Resources>
<DataTemplate x:Key="cellTemplate">
<TextBlock Text="{Binding Description}"/>
</DataTemplate>
<DataTemplate x:Key="cellEditingTemplate">
<local:MultiLineTextBox Text="{Binding Description}"/>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="Company" Binding="{Binding Name}"/>
<DataGridTemplateColumn Header="Description"
CellTemplate="{StaticResource cellTemplate}"
CellEditingTemplate="{StaticResource cellEditingTemplate}"/>
</DataGrid.Columns>
</DataGrid>
<Button Grid.Row="1" Content="Add" Command="{Binding AddCommand}"/>
</Grid>
MultiLineTextBox is the TextBox which i inherit from textbox, and override OnKeyDown method.
MultiLineTextBox's code:
public class MultiLineTextBox : TextBox
{
/// <summary>
/// On Key Down.
/// </summary>
/// <param name="e"></param>
protected override void OnKeyDown ( KeyEventArgs e )
{
base.OnKeyDown ( e );
string oldText = Text;
ModifierKeys keys = Keyboard.Modifiers;
if ( e.Key == Key.Enter )
{
if ( ( Keyboard.Modifiers & ModifierKeys.Control ).Equals ( ModifierKeys.Control ) )
{
int index = SelectionStart;
oldText = oldText.Insert ( index, Environment.NewLine );
Text = oldText;
Select ( index + 1, 0 );
e.Handled = true;
}
else
{
e.Handled = false;
}
}
else if ( e.Key == Key.Escape )
{
Text = oldText;
e.Handled = false;
}
}
}
I don't know why but the previous answer didn't work in my case. I found and alternative solution here http://madcoderspeak.blogspot.ca/2010/04/set-keyboard-focus-when-user-begins.html
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel>
<TextBox x:Name="editCommentTextBox" Text="{Binding Comment, Mode=TwoWay}"
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">
</TextBox>
<Label Content="{Binding Text, ElementName=editCommentTextBox, Converter={StaticResource CharCounterConverter}}"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
I find a way to resolve this problem, and here is my code.
<Window x:Class="MultiLineEditDataGrid.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MultiLineEditDataGrid"
Title="MainWindow" Height="350" Width="525">
<Grid DataContext="{Binding Source={x:Static Application.Current}, Path=CompanyManager}">
<Grid.RowDefinitions>
<RowDefinition Height="270"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding Companies}" CanUserAddRows="False" AutoGenerateColumns="False">
<DataGrid.Resources>
<DataTemplate x:Key="cellTemplate">
<TextBlock Text="{Binding Description}"/>
</DataTemplate>
<DataTemplate x:Key="cellEditingTemplate">
<local:MultiLineTextBox x:Name="multiLineTxt" Text="{Binding Description}"/>
<DataTemplate.Triggers>
<Trigger SourceName="multiLineTxt" Property="IsVisible" Value="True">
<Setter TargetName="multiLineTxt" Property="FocusManager.FocusedElement" Value="{Binding ElementName=multiLineTxt}"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="Company" Binding="{Binding Name}"/>
<DataGridTemplateColumn Header="Description"
CellTemplate="{StaticResource cellTemplate}"
CellEditingTemplate="{StaticResource cellEditingTemplate}"/>
</DataGrid.Columns>
</DataGrid>
<Button Grid.Row="1" Content="Add" Command="{Binding AddCommand}"/>
</Grid>
The following simple datatemplate only works at runtime. At designtime it displays nothing. Why is this so?
<DataTemplate x:Key="SomeEnumDataTemplate">
<ListBox Name="list" Width="20" IsSynchronizedWithCurrentItem="True" SelectedIndex="{Binding Mode=OneWay, Converter={StaticResource EnumToIntConverter}}">
<ListBox.Template>
<ControlTemplate TargetType="ListBox">
<ContentPresenter Content="{TemplateBinding SelectedItem}" />
</ControlTemplate>
</ListBox.Template>
<Rectangle Height="10" Width="10" Fill="Red" />
<Rectangle Height="10" Width="10" Fill="Green" />
<Rectangle Height="10" Width="10" Fill="Yellow" />
</ListBox>
</DataTemplate>
I use it like this in another DataTemplate:
<HierarchicalDataTemplate x:Key="NodeDataTemplate" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" ToolTip="{Binding Description}">
<ContentControl ContentTemplate="{StaticResource SomeEnumDataTemplate}" Content="{Binding Mode}" Margin="3,0,0,0" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
Which again is used in a UserControl that has design-time data:
<UserControl x:Class="MyProject.Views.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:ViewModels="clr-namespace:MyProject.ViewModels" mc:Ignorable="d"
d:DesignHeight="780" d:DesignWidth="400" d:DataContext="{x:Static ViewModels:SampleData.RootNode}">
<TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource NodeDataTemplate}">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</UserControl>
You can easily create design time data:
Create your DataModel:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public class PersonCollection : List<Person>
{
public PersonCollection()
{
}
}
Create a file with .xaml extension containing:
DesignTimeTreeData.xaml
<local:PersonCollection xmlns:local="clr-namespace:Test_TreeWithDesignData">
<local:Person Name="Joan Solo" Age="32" />
<local:Person Name="Amara Skywalker" Age="31" />
</local:PersonCollection>
Use the d:DataContext and d:DesignData to use the data you specified in the DesignTimeTreeData.xaml:
MainWindow.xaml
<Window x:Class="Test_TreeWithDesignData.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:Test_TreeWithDesignData"
Title="MainWindow"
Height="350"
Width="525"
mc:Ignorable="d">
<Grid>
<TreeView
d:DataContext="{d:DesignData Source=./DesignTimeTreeData.xaml}"
ItemsSource="{Binding}">
<TreeView.Resources>
<DataTemplate DataType="{x:Type local:Person}" >
<StackPanel Orientation="Horizontal" Height="25">
<Label Content="{Binding Name}"/>
<Label Content="{Binding Age}" Margin="3,0,0,0"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
</Window>
Since tipically the Window.DataContext property is set to a ViewModel and the TreeView.DataContext is a collection of it, in order yo keep both datasources working, you can surround the TreeView with a Grid which DataContext is set to the ViewModel's collection.
DummyViewModel.cs
public class DummyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public PersonCollection Persons { get; set; }
public DummyViewModel()
{
this.Persons = new PersonCollection();
}
}
MainWindow.xaml
<Window x:Class="Test_TreeWithDesignData.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:Test_TreeWithDesignData"
Title="MainWindow"
Height="350"
Width="525"
mc:Ignorable="d">
<Window.DataContext>
<local:DummyViewModel />
</Window.DataContext>
<Grid Name="RootGrid">
<Grid Name="TreeGrid" DataContext="{Binding Persons}">
<TreeView d:DataContext="{d:DesignData Source=./DesignTimeTreeData.xaml}"
ItemsSource="{Binding}">
<TreeView.Resources>
<DataTemplate DataType="{x:Type local:Person}" >
<StackPanel Orientation="Horizontal" Height="25">
<Label Content="{Binding Name}"/>
<Label Content="{Binding Age}" Margin="3,0,0,0"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
</Grid>
</Window>
Result:
Edit
The next question is going to be: Ho do I expand the items in the Designer?
The Person is going to have a collection of persons:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public PersonCollection Childs { get; set; }
}
The design time data is going to have a child
DesignTimeTreeData.xaml
<local:PersonCollection xmlns:local="clr-namespace:Test_TreeWithDesignData">
<local:Person Name="Joan Solo" Age="32" />
<local:Person Name="Amara Skywalker" Age="31">
<local:Person.Childs>
<local:PersonCollection>
<local:Person Name="Han Skywalker" Age="10" />
</local:PersonCollection>
</local:Person.Childs>
</local:Person>
</local:PersonCollection>
The tree is going to have a HierarchicalDataTemplate now:
<HierarchicalDataTemplate
DataType="{x:Type local:Person}"
ItemsSource="{Binding Childs}">
<StackPanel Orientation="Horizontal" Height="25">
<Label Content="{Binding Name}"/>
<Label Content="{Binding Age}" Margin="3,0,0,0"/>
</StackPanel>
</HierarchicalDataTemplate>
And the TreeView will bind to the DesignerProperties.IsInDesignMode to expand the items in the designer:
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource Self}, Path=(pf:DesignerProperties.IsInDesignMode)}"
Value="true"
>
<Setter Property="IsExpanded" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
This is the xaml for the window:
MainWindow.xaml
<Window x:Class="Test_TreeWithDesignData.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:Test_TreeWithDesignData"
xmlns:pf="clr-namespace:System.ComponentModel;assembly=PresentationFramework"
Title="MainWindow"
Height="250"
Width="325"
mc:Ignorable="d"
>
<Window.DataContext>
<local:DummyViewModel />
</Window.DataContext>
<Grid Name="RootGrid">
<Grid Name="TreeGrid" DataContext="{Binding Persons}">
<TreeView
d:DataContext="{d:DesignData Source=./DesignTimeTreeData.xaml}"
ItemsSource="{Binding}"
>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource Self}, Path=(pf:DesignerProperties.IsInDesignMode)}"
Value="true"
>
<Setter Property="IsExpanded" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type local:Person}"
ItemsSource="{Binding Childs}"
>
<StackPanel Orientation="Horizontal" Height="25">
<Label Content="{Binding Name}"/>
<Label Content="{Binding Age}" Margin="3,0,0,0"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
</Grid>
</Window>
And this is the result:
Once you start Binding, setting DataContext, ItemSource etc, the designer will crap out on you. just remove all your bindings (looks like 3) and your designer will work. Get it all lined up, or whatever you need to do, then add bindings back.
I am trying to make a list box where I have complete control over the look of each item in the list box.
I can make the item horizontally stretch. However, there's this thin sliver of blue to the left of a selected item. (In the picture, the middle item is selected).
Can I make this blue strip go away?
alt text http://img44.imageshack.us/img44/949/boundlistboxdisplay.jpg
Here's the complete code.
<Window x:Class="SimpleListTemplate.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Window.Resources>
<XmlDataProvider x:Key="DcCharacters">
<x:XData>
<Characters xmlns="">
<Character HeroName="Catwoman" Identity="Selina Kyle" />
<Character HeroName="Batman" Identity="Bruce Wayne" />
<Character HeroName="Starman" Identity="Jack Knight" />
</Characters>
</x:XData>
</XmlDataProvider>
</Window.Resources>
<Grid>
<ListBox
ItemsSource="{Binding Source={StaticResource DcCharacters}, XPath=//Characters/*}"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Label
Content="{Binding XPath=#HeroName}"
Height="40"
VerticalContentAlignment="Center"
Background="LightGreen"
BorderThickness="2"
BorderBrush="DarkGreen"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Here is an update for your code.
<Window x:Class="SimpleListTemplate.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<XmlDataProvider x:Key="DcCharacters">
<x:XData>
<Characters xmlns="">
<Character HeroName="Catwoman" Identity="Selina Kyle" />
<Character HeroName="Batman" Identity="Bruce Wayne" />
<Character HeroName="Starman" Identity="Jack Knight" />
</Characters>
</x:XData>
</XmlDataProvider>
<Style TargetType="{x:Type ListBoxItem}" x:Key="ContainerStyle">
<Setter Property="Padding" Value="0,0,0,0"/>
</Style>
</Window.Resources>
<Grid>
<ListBox
ItemsSource="{Binding Source={StaticResource DcCharacters}, XPath=//Characters/*}"
ItemContainerStyle="{StaticResource ContainerStyle}"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Label
Content="{Binding XPath=#HeroName}"
Height="40"
VerticalContentAlignment="Center"
Background="LightGreen"
BorderThickness="2"
BorderBrush="DarkGreen"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
This may solve your problem.