Hiding context menu based on data trigger - wpf

I have a couple of grids in a user control. I want to hide the context menu in a grid based on a property in the DataContext. I have this code:
<Style TargetType="{x:Type ContextMenu}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsLockedNorthGrid}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
I know the IsLockedNorthGrid property works since I'm using it somewhere else in xaml. What am I missing?
Thanks

If you are using Style.Triggers to change the Visibility, be sure you are not setting Visibility to Context Menu inline. Since, inline property has higher priority over the style.

Check if the Visibility is set to a specific value (setting it in a specific element and triggering it won´t work). Also try
<Style TargetType="{x:Type ContextMenu}">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsLockedNorthGrid}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>

I was able to solve my problem like so:
<XamDataGrid ContextMenuOpeninig="OnContextMenuOpening">
<XamDataGrid.Resources>
<ContextMenu x:Key="GridContextMenu">...</ContextMenu> </XamDataGrid.Resources>
<XamDataGrid>
Code behind:
private void OnContextMenuOpening(object sender, ContextMenuEventArgs e)
{
var logViewModel = (LogViewModelBase)DataContext;
var grid = sender as XamDataGrid;
var menu = grid.Resources["GridContextMenu"] as ContextMenu;
menu.Visibility = !logViewModel.IsLockedNorthGrid ? Visibility.Hidden : Visibility.Visible;
}
Not that pretty but it works.

Related

Conditionally create view in XAML?

I have views that may or may not be created in xaml based on a boolean condition in codebehind or a viewmodel.
I'd like to do something like:
<AlwaysVisibleView />
<IfShowSometimesViewBindingOrVariableOrSomething>
<SometimesView AProperty="something"/>
</IfShowSometimesViewBindingOrVariableOrSomething>
I'd like to implement this avoiding codebehind and other such trickery as much as possible, at the last ideally I don't want the view to be instantiated.
Dynamic creation of views can sometimes get a little tricky. Plus they tend to mess up how XAML renders things.
Can you just bind the visibility of the "Sometimes visible view" to a property? You can run that through a Boolean to visibility convertor and just have the code behind toggle the bool to show/hide.
Example thread
You can work with ContentControl and Style.Triggers to change content and visibility based on a property (example: bool ShowMe):
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Content" Value="{x:Null}"/>
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowMe}" Value="True">
<Setter Property="Content">
<Setter.Value>
<SometimesView AProperty="something"/>
</Setter.Value>
</Setter>
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>

Grid Hide/Show animation

I've got some User Control, which is grid with transparent background, on top of that there's another grid that is suppose to change its width when triggers are fired (ultimately it'll be a in/out animation). Purpose was to simply use something different than Visibility that was instantly hiding entire control without any animations Here's the property it's based on:
public static readonly DependencyProperty IsOpenedProperty = DependencyProperty.Register ("IsOpened", typeof (Boolean), typeof (SidePanel), new PropertyMetadata (false));
public bool IsOpened {
get {
if (GetValue (IsOpenedProperty) != null)
return (bool)GetValue (IsOpenedProperty);
else
return false;
}
set {
SetValue (IsOpenedProperty, value);
Console.WriteLine(value);
}
}
And the Grid itself:
<Grid>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Setter Property="Width" Value="50"/>
<Setter Property="Background" Value="Gray"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsOpened}" Value="True">
<Setter Property="Width" Value="350"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsOpened}" Value="False">
<Setter Property="Width" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="{Binding ElementName=Side, Path=Width}"/>
</Grid>
Console gives me corect output but triggers don't fire at all.
Just checked one more thing and when I set trigger for x:Null value then it's fired all the time, how is it possible as Get shouldn't ever return null value here?
Alright I just gave up on dependency property here, created private variable to hold the value and added data context with INotifyPropertyChanged interface implementation and now it works.

INotifyPropertyChange ignored in binding to a DataTrigger

I'm working on some code that has a Button that contains an image and some text, and which should display either the image, the text, or both, depending upon the value of a bound property. The code is currently using Styles and DataTriggers:
public enum ButtonStyle { Image, Text, Both };
public class ViewModel : INotifyPropertyChanged
{
private ButtonStyle _buttonStyle;
public ButtonStyle buttonStyle
{
get { return this._buttonStyle; }
set
{
this._buttonStyle = value;
notifyPropertyChanged("buttonStyle");
}
}
And:
<UserControl.Resources>
<Style x:Key="buttonTextStyle" TargetType="{x:Type Label}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=buttonStyle}"
Value="{x:Static local:ButtonStyle.Text}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="buttonImageStyle" TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=buttonStyle}"
Value="{x:Static local:ButtonStyle.Image}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Button>
<StackPanel>
<Image Source="..." Style="{StaticResource buttonImageStyle} />
<Label Style={StaticResource buttonTextStyle}>My Text</Label>
</StackPanel>
</Button>
My problem? The button doesn't change when I change the value of the buttonStyle property in the view model. This control is in a tab, and if I switch to another tab and then switch back, the button updates to reflect the current value of the buttonStyle property, but it does not change until I do.
It looks like the DataTrigger is processed only when the control is rendered, and does not re-render when the bound value is modified, despite the bound value raising a PropertyChanged event.
Any ideas?
Try NotifyOnSourceUpdated=True on each of your data triggers.
<DataTrigger Binding="{Binding Path=buttonStyle, NotifyOnSourceUpdated=True}"
Value="{x:Static local:ButtonStyle.Text}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
I think this is a nicer way to refer to enums in your DataTrigger:
<Style x:Key="buttonImageStyle" TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=buttonStyle}">
<DataTrigger.Value>
<local:ButtonStyle>Text</local:ButtonStyle>
</DataTrigger.Value>
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
The value of the resource changes during runtime, thats why you should use DynamicResource instead of StaticResource:
Style="{DynamicResource buttonImageStyle}"
Here's an idea - any time you have a binding problem and it looks like INotifyPropertyChanged isn't working, check and double check and make damned sure that you spelled the name of the property right, in your PropertyChangedEventArgs().
Sorry for the trouble.

How to make a WPF DataGrid with alternating row colors, and a fix color section (ignoring alternation)

I have a datagrid that I'm trying to make resemble:
I'm using the AlternatingRowBackground attribute to perform the alternating colors. For the fixed color section, I have XAML that resembles:
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ShouldBeFixedColor}" Value="True">
<DataTrigger.Setters>
<Setter Property="Background" Value="Blue" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
The problem with this approach is that the "alternating color" takes precedence over the fixed color style trigger. So, at the bottom instead of blue-blue-blue it is blue-gray-blue.
Any ideas on how to archive the desired coloring? I'd rather do this all at the XAML level if possible.
Thanks!
Made some changes based upon other SO answers. Hopefully this helps someone in the future.
Yank AlternatingRowBackground=... from the grid. Add AlternationCount="2"
Add the block below to do the styling (manually doing the alternating rows)
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<Trigger Property="AlternationIndex" Value="0">
<Setter Property="Background" Value="White" />
</Trigger>
<Trigger Property="AlternationIndex" Value="1">
<Setter Property="Background" Value="WhiteSmoke" />
</Trigger>
<DataTrigger Binding="{Binding Path=Selectable}" Value="False">
<DataTrigger.Setters>
<Setter Property="Background" Value="LightGray" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
In case someone else is also looking for the same thing done in code:
Style rowStyle = new Style(typeof(DataGridRow));
Trigger rowTrigger = new Trigger();
rowTrigger.Property = DataGridRow.AlternationIndexProperty;
rowTrigger.Value = 0;
Setter rowSetter = new Setter(DataGridRow.BackgroundProperty, Brushes.Yellow);
rowTrigger.Setters.Add(rowSetter);
Trigger alternateRowTrigger = new Trigger();
alternateRowTrigger.Property = DataGridRow.AlternationIndexProperty;
alternateRowTrigger.Value = 1;
Setter alternateRowSetter = new Setter(DataGridRow.BackgroundProperty, Brushes.Pink);
alternateRowTrigger.Setters.Add(alternateRowSetter);
DataTrigger rowDataTrigger = new DataTrigger();
rowDataTrigger.Value = true;
rowDataTrigger.Binding = new Binding() { Path = new PropertyPath(nameof(MyObject.IsSomethingTrue))};
Setter backgroundSetter = new Setter(DataGridRow.BackgroundProperty, Brushes.Blue);
Setter foregroundSetter = new Setter(DataGridRow.ForegroundProperty, Brushes.White);
rowDataTrigger.Setters.Add(backgroundSetter);
rowDataTrigger.Setters.Add(foregroundSetter);
// the order of the triggers may not be changed as explained by CptCoathanger
rowStyle.Triggers.Add(rowTrigger);
rowStyle.Triggers.Add(alternateRowTrigger);
rowStyle.Triggers.Add(rowDataTrigger);
RootDataGridOrders.RowStyle = rowStyle;

How to add new setter programatically in existing ItemContainerStyle in WPF?

I have item container style for list view like below :
<ListView.ItemContainerStyle>
<Style>
<Setter Property="Padding" Value="0"></Setter>
<Setter Property="Margin" Value="0,0,0,-1"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=EventType}" Value="2">
<Setter Property="Background" Value="Green"/>
<Setter Property="Foreground" Value="White"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
Now I need to add new setter for context menu programatically based on item data. How can I do it ?
Please guide me.....
Thanks
I have to display Images dynamically and show/hide depends on state of checkboxes using Style and DataTrigger.
<Image Source="/WpfApplication;component/Imgs/img1_1.png">
<Image.Style>
<Style>
<Setter Property="Image.Visibility" Value="Collapsed"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=isDisplayingImgSet1, Path=IsChecked}" Value="True">
<Setter Property="Image.Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
After spec modification i have to create checkboxes dynamically, then to set Style.
At beginning, I have the same error message.
Image img = new Image();
//..
img.Style.Setters.Add(setter);
img.Style.Triggers.Add(trigger);
//..After a 'SetterBaseCollection' is in use (sealed), it cannot be modified.
The solution is to create a style object and Affect it to Image.Style
//Visibility
DataTrigger trigger = new DataTrigger();
trigger.Binding = new Binding
{
ElementName = "isDisplayingImgSet"+NumSet,
Path = new PropertyPath(CheckBox.IsCheckedProperty)
};
trigger.Value = "True";
trigger.Setters.Add(new Setter(ContentControl.VisibilityProperty, Visibility.Visible));
Setter setter = new Setter(ContentControl.VisibilityProperty, Visibility.Collapsed);
Style style = new Style(typeof(Image));
style.Setters.Add(setter);
style.Triggers.Add(trigger);
img.Style = style;
Hope that helping you
PS : It's my first post
I have implemented logic to add setter dynamically in existing style like below :
listview1.ItemContainerStyle.Setters.Add(new Setter(Control.ContextMenuProperty,GetItemContextMenu(txtName.Text)));
But it gives me following error :
"After a 'SetterBaseCollection' is in use (sealed), it cannot be modified."
I think it is not possible to add new setter in sealed style. So I have got another temporary solution like assign context menu to whole listview rather than it's item on preview mouse right click event. So context menu will be same for all items then managed in coding based on data of selected item.
<ListView.ItemContainerStyle>
<Style>
<Setter Property="Padding" Value="0"></Setter>
<Setter Property="Margin" Value="0,0,0,-1"></Setter>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=EventType}" Value="2">
<Setter Property="Background" Value="Green"/>
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
If you create a new Style() and "re-add" the the setters, you can get around this issue if you are using Telerik.
private static void AddColumnSetter(GridViewColumn gridColumn, SetterBase setter)
{
var setters = new SetterBaseCollection();
if (gridColumn.CellStyle != null)
{
foreach (SetterBase setterBase in gridColumn.CellStyle.Setters)
{
setters.Add(setterBase);
}
}
setters.Add(setter);
gridColumn.CellStyle = new Style();
foreach (var setr in setters)
{
gridColumn.CellStyle.Setters.Add(setr);
}
}

Resources