I need to change the layout of my window based on what the user selects in a combo box. I've made a stab at what one way might be but feel like it is clunky and hacked together. Im certain their must be a cleaner MVVM solution.
My thoughts where to have multiple dock panels in my GroupBox Whose visibility is set to collapse. When the selection is made, the appropriate dockpanel will be set to visible. I attempted to find a way to do this inside the view model with no success. I also couldn't help but think my attempts are violating MVVM.
XAML
<GroupBox Header="Options">
<Grid>
<DockPanel LastChildFill="False" x:Name="syncWellHeadersDockPanel" Visibility="Collapsed">
<Button DockPanel.Dock="Right" Content="Test"></Button>
</DockPanel>
<DockPanel LastChildFill="False" x:Name="SyncDirectionalSurveyDockPanel" Visibility="Collapsed">
<Button DockPanel.Dock="Left" Content="Test02"></Button>
</DockPanel>
</Grid>
</GroupBox>
ViewModel - Property for Selected Item for ComboBox
private StoredActionsModel _selectedStoredAction = DefaultStoredAction.ToList<StoredActionsModel>()[0];
public StoredActionsModel SelectedStoredAction
{
get { return _selectedStoredAction; }
set
{
if (value != _selectedStoredAction)
{
// Unset Selected on old value, if there was one
if (_selectedStoredAction != null)
{
_selectedStoredAction.Selected = false;
}
_selectedStoredAction = value;
// Set Selected on new value, if there is one
if (_selectedStoredAction != null)
{
_selectedStoredAction.Selected = true;
}
OnPropertyChanged("SelectedStoredAction");
if (_selectedStoredAction.StoredActionID == 4)
{
//X:SyncWellHeaderDockPanel.visibility = true?????
}
}
}
}
Here's a pure-XAML way to do exactly what you're asking how to do. It's a bit verbose.
Notice that we no longer set Visibility in attributes on the DockPanels. If we still did that, the values set in the Style trigger would be overridden by the attributes. That's the way dependency properties work.
<GroupBox Header="Options">
<Grid>
<DockPanel LastChildFill="False" x:Name="syncWellHeadersDockPanel" >
<Button DockPanel.Dock="Right" Content="Test"></Button>
<DockPanel.Style>
<Style TargetType="DockPanel" >
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger
Binding="{Binding SelectedStoredAction.StoredActionID}"
Value="1"
>
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
</DockPanel>
<DockPanel LastChildFill="False" x:Name="SyncDirectionalSurveyDockPanel">
<Button DockPanel.Dock="Left" Content="Test02"></Button>
<DockPanel.Style>
<Style TargetType="DockPanel" >
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger
Binding="{Binding SelectedStoredAction.StoredActionID}"
Value="2"
>
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
</DockPanel>
</Grid>
</GroupBox>
Another way to do this would be to pass SelectedStoredAction.StoredActionID to a DataTemplateSelector, but that involves writing C# code that knows what your XAML resource keys are, and I'm not a fan.
Related
I have a listbox that loads it's items with Foreground color set to red. What I'd like to do is: upon selecting an item with the mouse, change the foreground color of SelectedItem to black, but make the change persistent so that after deselecting the item, color remains black. Incidentally I want to implement this as a way of showing 'read items' to the user.
Essentially I want something like an implementation of the common property trigger like the code below, but not have the style revert after deselection. I've played around with event triggers as well without much luck.
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True" >
<Setter Property="Foreground" Value="Black" /> //make this persist after deselection
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
Thanks in advance!
You could animate the Foreground property:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(ListBoxItem.Foreground).(SolidColorBrush.Color)"
To="Black" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
The downside of this simple approach is that the information is not stored somewhere. This is pure visualization without any data backing. In order to persist the information, so that restarting the application shows the same previous state, you should introduce a dedicated property to your data model e.g IsMarkedAsRead.
Depending on your requirements, you can override the ListBoxItem.Template and bind ToggleButton.IsChecked to IsMarkedAsRead or use a Button which uses a ICommand to set the IsMarkedAsRead property. There are many solutions e.g. implementing an Attached Behavior.
The following examples overrides the ListBoxItem.Template to turn the ListBoxItem into a Button. Now when the item is clicked the IsMarkedAsRead property of the data model is set to true:
Data model
(See Microsoft Docs: Patterns - WPF Apps With The Model-View-ViewModel Design Pattern for an implementation example of the RelayCommand.)
public class Notification : INotifyPropertyChanged
{
public string Text { get; set; }
public ICommand MarkAsReadCommand => new RelayCommand(() => this.IsMarkedAsRead = true);
public ICommand MarkAsUnreadCommand => new RelayCommand(() => this.IsMarkedAsRead = false);
private bool isMarkedAsRead;
public bool IsMarkedAsRead
{
get => this.isMarkedAsRead;
set
{
this.isMarkedAsRead = value;
OnPropertyChanged();
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
ListBox
<ListBox ItemsSource="{Binding Notifications}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Background="{TemplateBinding Background}">
<Button x:Name="ContentPresenter"
ContentTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox}, Path=ItemTemplate}"
Content="{TemplateBinding Content}"
Command="{Binding MarkAsReadCommand}"
Foreground="Red">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border>
<ContentPresenter />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsMarkedAsRead}" Value="True">
<Setter TargetName="ContentPresenter" Property="Foreground" Value="Green" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type Notification}">
<TextBlock Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thanks a lot #BionicCode for the comprehensive answer. I ended up going with another solution which may or may not be good convention; I am a hobbyist.
Firstly, I don't need databacking / persistence.
Concerning the data model solution and overriding ListBoxItem.Template, I am using a prededfined class 'SyndicationItem' as the data class (my app is Rss Reader). To implement your datamodel solution I guess I could hack an unused SyndicationItem property, or use SyndicationItem inheritance for a custom class (I'm guessing this is the most professional way?)
My complete data model is as follows:
ObservableCollection >>> CollectionViewSource >>> ListBox.
Anyway I ended up using some simple code behind which wasn't so simple at the time:
First the XAML:
<Window.Resources>
<CollectionViewSource x:Key="fooCollectionViewSource" Source="{Binding fooObservableCollection}" >
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="PublishDate" Direction="Descending" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<Style x:Key="DeselectedTemplate" TargetType="{x:Type ListBoxItem}">
<Setter Property="Foreground" Value="Gray" />
</Style>
</Window.Resources>
<ListBox x:Name="LB1" ItemsSource="{Binding Source={StaticResource fooCollectionViewSource}}" HorizontalContentAlignment="Stretch" Margin="0,0,0,121" ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<TextBlock MouseDown="TextBlock_MouseDown" Grid.Column="0" Text="{Binding Path=Title.Text}" TextWrapping="Wrap" FontWeight="Bold" />
<TextBlock Grid.Column="1" HorizontalAlignment="Right" TextAlignment="Center" FontSize="11" FontWeight="SemiBold"
Text="{Binding Path=PublishDate.LocalDateTime, StringFormat='{}{0:d MMM, HH:mm}'}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now the code behind:
Solution 1: this applies a new style when listboxitem is deselected. Not used anymore so the LB1_SelectionChanged event is not present in the XAML.
private void LB1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count != 0)
{
foreach (var lbItem in e.RemovedItems)
{
//get reference to source listbox item. This was a pain.
int intDeselectedItem = LB1.Items.IndexOf(lbItem);
ListBoxItem lbi = (ListBoxItem)LB1.ItemContainerGenerator.ContainerFromIndex(intDeselectedItem);
/*apply style. Initially, instead of applying a style, I used mylistboxitem.Foreground = Brushes.Gray to set the text color.
Howver I noticed that if I scrolled the ListBox to the bottom, the text color would revert to the XAML default style in my XAML.
I assume this is because of refreshes / redraws (whichever the correct term). Applying a new style resolved.*/
Style style = this.FindResource("DeselectedTemplate") as Style;
lbi.Style = style;
}
}
}
Solution 2: The one I went with. Occurs on SelectedItem = true, same effect as your first suggestion.
private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
TextBlock tb = e.Source as TextBlock;
tb.Foreground = Brushes.Gray;
}
I have a UserControl that is a ListView with a Canvas as the ItemsPanel. The ItemsSource of the ListView is bound to a CollectionViewSource with it's Source property bound to an ObservableCollection< DeviceItem >. DeviceItem is a custom class that, among other things, holds the X/Y coordinates of the ListViewItem/DeviceItem which gets serialized when the user wants to save their work. On PreviewMouseDown and PreviewTouchDown I check the ListViewItem that's being clicked/touched to make sure that the content of the ListViewItem is a DeviceItem so that as the user moved it around the screen the software can updates the coordinates on the DeviceItem. This works 99% of the time but I run into a problem (sometimes) after the user removes items from the collection and then adds new items. After removing/adding, when the user clicks/touches the ListViewItem it's Content is typeof MS.Internal.NamedObject and in that case I can't act on the item. I'm sure it's worth mentioning that the user is adding/removing from a separate control in another window that is bound to the same collection in our data model (ObservableCollection< DeviceItem >).
Currently the user has to close and reopen the software when this happens. I'm trying to find a way to recover without restarting the software.
<UserControl.Resources>
<Style x:Key="CanvasStyle" TargetType="{x:Type Canvas}">
<EventSetter Event="Loaded" Handler="Canvas_Loaded"/>
</Style>
<CollectionViewSource Source="{Binding DiagramCollection}" x:Key="radioPacksDataView" />
</UserControl.Resources>
<Grid>
<ListView Name="listBoxDevices"
Background="Transparent"
BorderThickness="0"
Stylus.IsPressAndHoldEnabled ="False"
ItemsSource="{Binding Source={StaticResource radioPacksDataView}}"
PreviewMouseMove="ListBox_PreviewMouseMove"
PreviewMouseDown="ListBox_PreviewMouseDown"
PreviewMouseUp="ListBox_PreviewMouseUp"
PreviewTouchDown="ListBox_PreviewTouchDown"
PreviewTouchMove="ListBox_PreviewTouchMove"
PreviewTouchUp="ListBox_PreviewTouchUp"
SnapsToDevicePixels="True"
IsHitTestVisible="{Binding AccessRights, ConverterParameter=Unlocked, Converter={StaticResource enumBooleanConverter}, Source={x:Static appData:Globals.CurrentUser}}"
VirtualizingPanel.IsVirtualizing="False"
IsSynchronizedWithCurrentItem="False">
<ListView.Style>
<Style>
<Setter Property="ItemsControl.ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Canvas Style="{DynamicResource CanvasStyle}"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=window, Path=IconStyle}" Value="0">
<Setter Property="ListView.ItemContainerStyle" Value="{DynamicResource MultiDeviceItemContainerStyle}" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=window, Path=IconStyle}" Value="1">
<Setter Property="ListView.ItemContainerStyle" Value="{DynamicResource MultiDeviceItemContainerStyleV2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
<ListView.LayoutTransform>
<ScaleTransform CenterX="0"
CenterY="0"
ScaleX="{Binding ZoomScale}"
ScaleY="{Binding ZoomScale}"/>
</ListView.LayoutTransform>
</ListView>
</Grid>
And then in code behind
private void ListBox_PreviewMouseDown(object sender, MouseEventArgs e)
{
if (m_selectedListViewItem == null)
{
m_selectedListViewItem = ItemsControl.ContainerFromElement((ListBox)sender, e.OriginalSource as DependencyObject) as ListViewItem;
//this is the section that fails after removing/adding items, object is typeof(MS.Internal.NamedObject)
if (m_selectedListViewItem.Content.GetType().IsSubclassOf(typeof(DeviceItem)))
{
m_selectedDeviceItem = m_selectedListViewItem.Content as DeviceItem;
}
}
}
I'm having an issue with a TabControl where I manage (in some special cases) to get two tabs headers selected (only one body showing afaik), and I can't change the selected tab.
Selected tabs have bold header text.
In this image, "Ämnesinformation" and "R43" are both selected.
My application is structured as follows:
I have some views:
MainView: The main view, contains the TabControl which only contains one item in the image.
SubstanceTabsView: One of these for every tab in MainView.
SubstanceView and ClassificationView: the first is used for the "Ämnesinformation", of which there is only one per substance. The second can have multiple instances, like "R43", "R12" etc.
I also have some viewModels:
MainViewModel: The VM for the MainView.
SubstanceTabsViewModel: The VM for the SubstanceTabsView, contains a set of IViewModels
SubstanceViewModel, ClassificationViewModel: both implement IViewModel, are VMs for SubstanceView and ClassificationView
Some relevant xaml code:
Here's the tabcontrol in MainView.xaml
<TabControl SelectedItem="{Binding Path=SelectedTab}" ItemsSource="{Binding Path=Tabs}" >
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Header}" >
</TextBlock>
<local:CrossButton Margin="3" Padding="0" Width="12" Command="{Binding CloseCommand}"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Name="Border"
Margin="0,0,-4,0"
Background="{Binding Path=HeaderBackground}"
BorderBrush="#A0A0A0"
BorderThickness="1,1,1,1"
CornerRadius="3,10,0,0" >
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"
RecognizesAccessKey="True"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Panel.ZIndex" Value="100" />
<Setter TargetName="Border" Property="Background" Value="{Binding HeaderBackground}" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="Yellow" />
<Setter TargetName="Border" Property="BorderBrush" Value="Black" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
<Setter Property="Foreground" Value="Green" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate DataType="{x:Type localViewModels:SubstanceTabsViewModel}">
<localViews:SubstanceTabsView />
</DataTemplate>
</TabControl.Resources>
</TabControl>
Here's how I control the connection between different views and viewmodels in SubstanceTabsView.xaml
<TabControl SelectedItem="{Binding Path=SelectedTab}" ItemsSource="{Binding Path=Tabs}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Header}" />
<local:CrossButton Margin="3" Padding="0" Width="12" Command="{Binding CloseCommand}"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.Resources>
<DataTemplate DataType="{x:Type localViewModels:ClassificationViewModel}">
<localViews:ClassificationView />
</DataTemplate>
<DataTemplate DataType="{x:Type localViewModels:SubstanceViewModel}">
<localViews:SubstanceView />
</DataTemplate>
</TabControl.Resources>
</TabControl>
Here's the code for SubstanceTabsViewModel.cs which controls the second level tabs, the setter for the selectedTab controls some logic which asks the user about changing from an unsaved tab:
private IViewModel selectedTab;
public IViewModel SelectedTab
{
get
{
return selectedTab;
}
set
{
MessageBoxResult rsltMessageBox = MessageBoxResult.Yes;
if (selectedTab != null && selectedTab.SaveNeeded() && selectedTab.Id != 0 && value != null && selectedTab is ClassificationViewModel)
{
rsltMessageBox = notifyUserService.Ask("Bedömning är ändrad men ej sparad vill du verkligen lämna fliken?", "Bedömning ändrad");
}
if (rsltMessageBox == MessageBoxResult.Yes)
{
selectedTab = value;
}
OnPropertyChanged("SelectedTab");
}
}
private ObservableCollection<IViewModel> tabs;
public ObservableCollection<IViewModel> Tabs
{
get
{
return tabs;
}
set
{
tabs = value;
OnPropertyChanged("Tabs");
}
}
Some things my investigations have resulted in: If I don't do the notifyUserService call (which results in a messagebox.show()), there is no problem, only one tab is selected. If I look at the SelectedItem of the TabControl, it is only one item, the item it "should" be in my situation.
I finally found someone else having a similar problem, as described here**, "Displaying a message box causes a nested message pump; which means that almost all processing resumes. Of course, we are in the middle of trying to change the selected item, so this can cause all sorts of out-of-order or reentrancy problems. This class of problems is difficult to fix, and we are not going to be able to fix this in our next release." So the problem was with using MessageBox:es in the selectedItem setter.
I guess using some clever workaround is the appropriate solution in this case.
** Update March 2022
The URL referenced by the original post is no longer valid. The content can now be found here: WPF TabControl bug
I am building a Windows phone 8 app that has a view model property of:
public bool IsReply {get; set;}
In my xaml code, I would like to distinguish two cases:
IsReply=True
<Grid Margin="0,0,0,0">
...
</Grid>
IsReply=False
<Grid Margin="40,0,0,0">
...
</Grid>
Basically, I would like to style the Grid element depending on the value of IsReply. I know that in WPF Style.Triggers exists, but apparently not in WP.
The solution I have right now is to have a duplicate copy of the entire grid code and set the visibility of each to a data converter. However, I feel this should be simpler to do.
The easiest way is to use a Style with Triggers:
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Margin" Value="40 0 0 0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsReply}" Value="True">
<Setter Property="Margin" Value="0 0 0 0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
You can bind the margin of the grid in your MVVM
<Grid Margin="{Binding margin}">
...
</Grid>
In your model
if(IsReply)
margin = new Thickness("0,0,0,0");
else
margin = new Thickness("40,0,0,0");
No need to create separate grids.
You can use DataTrigger, but you have to add these two references (right click on References in your project and AddReference/Assemblies/Extensions/ ... ).
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<Grid
Margin="0">
<i:Interaction.Triggers>
<ei:DataTrigger
Binding="{Binding Path=IsReply}"
Value="True">
<ei:ChangePropertyAction
PropertyName="Margin"
Value="0" />
</ei:DataTrigger>
<ei:DataTrigger
Binding="{Binding Path=IsReply}"
Value="False">
<ei:ChangePropertyAction
PropertyName="Margin"
Value="40,0,0,0" />
</ei:DataTrigger>
</i:Interaction.Triggers>
</Grid>
When a button gets pressed the background (Rectangle Fill) of the button changes. So the user can see what buttons have been pressed and what not.
Problem:
I use a trigger and a Togglebutton to perform the "IsChecked" to change the background.
But the background change may only happens 1 time.
For example:
Button Background = black --> PRESS --> Button Background = blue
But when i press the button again the Button BackGround changes back to black (since its a ToggleButton).
How can i make sure the background only changes 1 time?
EDIT: The button must remain enabled, because a user gives in a reason when they press the button. Meaning if they pick the wrong reason they are able to change it.
<Style x:Key="ButtonStyleReg" TargetType="{x:Type myClasses:RegButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid x:Name="registrationButton">
<Rectangle Name="rectangleBtn" Fill="#FF89959A" Height="Auto" RadiusY="15" RadiusX="15" Stroke="White" Width="Auto"/>
<TextBlock x:Name="reason" TextWrapping="Wrap"
Text="{Binding Reason, StringFormat=\{0\}}"
HorizontalAlignment="Center" Margin="0,7.5,0,0" Height="Auto"
VerticalAlignment="Top" FontWeight="Bold" >
</TextBlock>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True"/>
<!--<Trigger Property="IsDefaulted" Value="True"/>-->
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="rectangleBtn" Property="Fill" Value="blue" />
</Trigger>
<Trigger Property="IsEnabled" Value="False"/>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="FontSize" Value="10.667"/>
Listbox that implements the style:
<ListBox x:Name="lbRegistration" ItemsSource="{Binding RegBtns, ElementName=Window}" Background="{x:Null}"
BorderBrush="{x:Null}" Grid.Column="1"
ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Disabled" Height="75">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<myClasses:RegistrationButton x:Name="RegistrationButton" HorizontalAlignment="Center" Height="71" Width="148"
Margin="10,0,5,0"
Style="{DynamicResource ButtonStyleRegistration}"
Click="RegistrationButton_Click"
Title="{Binding Title}"
Oorzaak="{Binding Oorzaak}"
DuurStilstand="{Binding DuurStilstand,
Converter={StaticResource DateTimeConverter}}"
BeginStilstand="{Binding BeginStilstand}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Best Regards,
Pete
Very simple would be if you can access the rectangle from code behind:
rectangleBtn.Fill = Brushes.Blue;
If you can't access it - make yourself two styles.
The one that is the original style, the other one is the Blue Style which shall be applied when the user clicks.
In the code behind, on event Click="RegistrationButton_Click"
Set the Style to the BlueStyle.
RegistrationButton.Style = this.FindResource("ButtonStyleRegistration") as Style;
Since you always want it blue, this code is enough as it is. It will always make it Blue for you.
The first time, and any other time.
That way you achieve your requirement and the style will change only once.
When the window is loaded, it will load into the original style (the first style).
So put in your XAML the style "Black" and in the code behind as shown above.
Then this must be removed:
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="rectangleBtn" Property="Fill" Value="blue" />
</Trigger>
Then this:
<myClasses:RegistrationButton x:Name="RegistrationButton" HorizontalAlignment="Center" Height="71" Width="148"
Margin="10,0,5,0"
Style="{DynamicResource ButtonStyleRegistration}"
Click="RegistrationButton_Click"
Shall be:
<myClasses:RegistrationButton x:Name="RegistrationButton" HorizontalAlignment="Center" Height="71" Width="148"
Margin="10,0,5,0"
Style="{DynamicResource ButtonStyleRegBlack}"
Click="RegistrationButton_Click"
That is all.
If you're using an MVVM approach I would recommend binding the background and the Click command to members in the ViewModel. The first time the button is clicked it sets a flag and changes the background color. The next time the button is clicked, if the flag is set, the command returns without changing the background.
XAML:
<ToggleButton Background="{Binding MyBackground}" Command="{Binding OnClickCmd}"/>
ViewModel
public class ViewModel : INotifyPropertyChanged
{
public Brush MyBackground
{
get { return background_; }
set {
background_ = value;
PropertyChanged(this, new PropertyChangedEventArg("MyBackground");
}
}
public ICommand OnClickCmd
{
get {
return new DelegateCommand(()=>{ // DelegateCommand implements ICommand
if(!isBackgroundSet) {
background = Brushes.Red;
isBackgroundSet_ = true;
}
});
}
}
private Brush background_;
private bool isBackgroundSet_;
private event PropertyChangedEventHandler PropertyChagned;
}