WPF custom window round corners - c#

I have a problem with my xaml style for my cutom window. The style sets the edges of the window to round and looks if it is docked to an edge. But it no longer works after changing the size of the window. On Start-up all edges are round, when I snap it to the right or the left of the screen then it shows the corners not rounded. But if I resize the window, it disables the round corners completely for the whole runtime.
How can I change my xaml code to get a behavoir that does not show rounded corners in snapped window style but if I resize my window?
The folowing code is my style code. You can set it to a window and resize the window how you like and you will see my problem with it?
<Style x:Key="ModernWindowStyle" TargetType="{x:Type Window}">
<Setter Property="AllowsTransparency" Value="True"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ResizeMode" Value="CanResize"/>
<Setter Property="WindowStyle" Value="None"/>
<Setter Property="MinHeight" Value="300"/>
<Setter Property="MinWidth" Value="530"/>
<Setter Property="MaxHeight" Value="{x:Static SystemParameters.MaximizedPrimaryScreenHeight}"/>
<Setter Property="MaxWidth" Value="{x:Static SystemParameters.MaximizedPrimaryScreenWidth}"/>
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome
CaptionHeight="50"
CornerRadius="8"
ResizeBorderThickness="5"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Window">
<Border BorderThickness="1"
BorderBrush="#FFD9D9D9">
<Border.Resources>
<core:MultiValueEqualityConverter x:Key="multiValueEqualityConverter" />
</Border.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="1" />
</Grid>
<Border.Style>
<Style TargetType="Border">
<Setter Property="CornerRadius"
Value="8" />
<Setter Property="Background" >
<Setter.Value>
<LinearGradientBrush EndPoint="0,0" StartPoint="0,1">
<GradientStop Color="#FF99BDCE" />
<GradientStop Color="White" Offset="0.7"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Value="False">
<Condition.Binding>
<MultiBinding Delay="1000" Converter="{StaticResource multiValueEqualityConverter}" ConverterParameter="HI">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="Width"
Mode="OneWay" />
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="RestoreBounds.Width"
Mode="OneWay" />
</MultiBinding>
</Condition.Binding>
</Condition>
<Condition Value="False">
<Condition.Binding>
<MultiBinding Delay="1000"
Converter="{StaticResource multiValueEqualityConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="Height"
Mode="OneWay" />
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="RestoreBounds.Height"
Mode="OneWay" />
</MultiBinding>
</Condition.Binding>
</Condition>
<Condition Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=WindowState}"
Value="Normal" />
</MultiDataTrigger.Conditions>
<Setter Property="CornerRadius"
Value="0" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the MultiValueEqualityConverter
public class MultiValueEqualityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values?.All(o => o?.Equals(values[0]) == true) == true || values?.All(o => o == null) == true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

I think the issue is RestoreBounds.Height or RestoreBounds.Width don't notify the value is changed, but Height or Width do.
So the MultiBinding "thinks" RestoreBounds was not changed and does use old values, what results to false.
To fix it just pass the window to the converter and check RestoreBounds there.
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Value="False">
<Condition.Binding>
<MultiBinding Delay="1000" Converter="{StaticResource multiValueEqualityConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="Height"
Mode="OneWay" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Mode="OneWay" />
</MultiBinding>
</Condition.Binding>
</Condition>
<Condition Value="False">
<Condition.Binding>
<MultiBinding Delay="1000" Converter="{StaticResource multiValueEqualityConverter}" ConverterParameter="HI">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="Width"
Mode="OneWay" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Mode="OneWay" />
</MultiBinding>
</Condition.Binding>
</Condition>
<Condition Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=WindowState}"
Value="Normal" />
</MultiDataTrigger.Conditions>
<Setter Property="CornerRadius"
Value="0" />
</MultiDataTrigger>
</Style.Triggers>
public class MultiValueEqualityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[1] is System.Windows.Window wnd)
{
return wnd.Height == wnd.RestoreBounds.Height && wnd.Width == wnd.RestoreBounds.Width;
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Related

How to disable validation of a combox element when visibility is collapsed?

I want to disable the validation of a ComboBox or a TextBox when their visibility is collapsed. Can I realize this within the XAML code?
<ComboBox Name="XYZCb" ItemsSource="{Binding XYZ}" Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}">
<ComboBox.Text>
<Binding Path="xyz" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<valid:ValidationRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</ComboBox.Text>
</ComboBox>
Button property binding:
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource InverseAndBooleansToBooleanConverter}"
Mode="TwoWay">
<Binding ElementName="XYZCb" Path="(Validation.HasError)"/>
</MultiBinding>
<Button.IsEnabled>
If I understand correctly what you need:
<Button>
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=XYZCb}" Value="True"/>
<Condition Binding="{Binding Visibility, ElementName=XYZCb}" Value="Visible"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Don't forget to remove your <Button.IsEnabled> binding.

User Prism Library With WPF, DataTrigger can not be triggered in ItemContainerStyle. Any Suggestion to get Instance of ViewModel

In MVVM pattern without Prism Library with WPF , view model is injected in manually. DataTrigger Path can be specified by
Path=DataContext.ColumnColorSlideStatus}". It works fine.
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListView}}, **Path=DataContext.ColumnColorSlideStatus}"**
Value="Review">
<Setter Property="Background">
<Setter.Value>
<Binding
Converter="{StaticResource ColumnBackGroundColorConverter}"
ConverterParameter="Review"
RelativeSource="{RelativeSource Self}" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListView}}, **Path=DataContext.ColumnColorSlideStatus}"**
Value="NFR">
<Setter Property="Background">
<Setter.Value>
<Binding
Converter="{StaticResource ColumnBackGroundColorConverter}"
ConverterParameter="NFR"
RelativeSource="{RelativeSource Self}" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
When Using Prism Library with WPF, the view and viewmodel is linked automatically by code below.
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
There is no instance of DataContext in View Xaml Code. the DataTrigger Path can not be specified to the DataContext.ColumnColorSlideStatus. DataTrigger can not be triggered.
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListView}}, **Path=ColumnColorSlideStatus}"**
Value="Review">
<Setter Property="Background">
<Setter.Value>
<Binding
Converter="{StaticResource ColumnBackGroundColorConverter}"
ConverterParameter="Review"
RelativeSource="{RelativeSource Self}" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListView}}, **Path=ColumnColorSlideStatus}"**
Value="NFR">
<Setter Property="Background">
<Setter.Value>
<Binding
Converter="{StaticResource ColumnBackGroundColorConverter}"
ConverterParameter="NFR"
RelativeSource="{RelativeSource Self}" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
```
Is any suggestions to get instance of viewmodel into view when Prism Library is used or some way works around it

ContextMenu passing multiple parameter to viewmodel

I have a canvas which is bound to a List of items. Each item has his own X and Y field and is painted as rectangles on the canvas.
Each item has its contextmenu which in this case is bound to a List and is filled dynamically (e.g.: "On", "Off").
I'm now trying to pass the sender (item where the Contextmenu is assigned to) and the string of the binding as CommandParameter the viewmodel.
e.g.: itemA, "On"
How should I do this?
Here is my code:
<ItemsControl
x:Name="Overlay"
Grid.Column="1"
GCextAp:Dragging.IsDragging="{Binding IsDragging, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ItemsSource="{Binding Path=MapElements, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas
localAp:MapProperties.GenerateMapElementFunc="{Binding CreateMapElementFunc}"
localAp:MapProperties.IsEditingMode="{Binding IsEditMode}"
localAp:MapProperties.ManipulationFinished="{Binding ManipulationFinishedDelegate}"
localAp:MapProperties.ScaleFactor="{Binding ElementName=Overlay, Path=DataContext.ScaleFactor}"
AllowDrop="True"
RenderOptions.BitmapScalingMode="LowQuality">
<Canvas.Style>
<Style TargetType="Canvas">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect
BlurRadius="8"
Direction="270"
ShadowDepth="2.5"
Color="#DDDDDD" />
</Setter.Value>
</Setter>
<Setter Property="Opacity" Value="1" />
<Setter Property="Background" Value="{x:Null}" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=Overlay, Path=(GCextAp:Dragging.IsDragging)}" Value="true" />
<Condition Binding="{Binding IsEditMode}" Value="true" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="WhiteSmoke" />
<Setter Property="Opacity" Value="0.1" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Canvas.Style>
<i:Interaction.Behaviors>
<localBehave:MapCanvasDropBehavior />
</i:Interaction.Behaviors>
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding DynamicX}" />
<Setter Property="Canvas.Top" Value="{Binding DynamicY}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle
Width="{Binding DynamicWidth}"
Height="{Binding DynamicHeight}"
Stroke="Black"
Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Canvas}}"
Visibility="{Binding IsVisible, Converter={StaticResource converter}}">
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding Image}" />
</Rectangle.Fill>
<i:Interaction.Behaviors>
<localBehave:MapElementMoveBehavior />
</i:Interaction.Behaviors>
<Rectangle.ContextMenu>
<ContextMenu>
<MenuItem Header="Commands" ItemsSource="{Binding Path=PlacementTarget.Tag.AvailableElementCommands, RelativeSource={RelativeSource AncestorType=ContextMenu}, UpdateSourceTrigger=PropertyChanged}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding}" />
<Setter Property="Command" Value="{Binding Path=PlacementTarget.Tag.CMD_MapElement, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
<Setter Property="CommandParameter" Value=" I have no idea" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</ContextMenu>
</Rectangle.ContextMenu>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Sorry about asking questions which are unclear or not usefull :-). Anyway I found the solution myself.
The command paramater should look like this:
<Setter Property="CommandParameter">
<Setter.Value>
<MultiBinding Converter="{StaticResource menuItemCommandConverter}">
<MultiBinding.Bindings>
<Binding Path="DataContext" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}" />
<Binding Path="Header" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>

Error while adding validation rules class to binding

I wanted to add validation rules to textBox ,the problem is that when I do the following code I got the following error message,
A value of type 'myValidations' cannot be added to a collection or dictionary of type 'Collection`1'.
The class myValidations roles is active.
What can be the problem?
<TextBox x:Name="Name" Grid.Column="4" Margin="0,50,0,0" Grid.Row="2" Style="{StaticResource tooltipError}">
<Binding ElementName="textBlock" Path="Text">
<Binding.ValidationRules>
<viewModel:MyValidationsRules/>
</Binding.ValidationRules>
</Binding>
</TextBox>
The text boxes are inherit from the following style:
<Style TargetType="TextBox">
<Setter Property="AcceptsReturn" Value="True"/>
<Setter Property="AllowDrop" Value="True"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Height" Value="44"/>
<Setter Property="Width" Value="199"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<EventSetter Event="PreviewDragEnter"
Handler="DropText_PreviewDragEnter"/>
<EventSetter Event="PreviewDrop"
Handler="DropText_PreviewDrop"/>
<EventSetter Event="PreviewDragOver"
</Style>
Not sure about your problem, but I think you have missed the <TextBox.Text> tag in your code.
<TextBox x:Name="Name" Grid.Column="4" Margin="0,50,0,0" Grid.Row="2" Style="{StaticResource tooltipError}">
<TextBox.Text>
<Binding ElementName="textBlock" Path="Text">
<Binding.ValidationRules>
<viewModel:MyValidationsRules ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
I Think the problem is that your MyValidationsRules doesn't inherit of ValidationRule or your class MyValidationsRules not override the Validate method. This class and this method must be plubics.
public class MyValidationsRules : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
}
}

How to make a WPF listbox item not clickable

I am using a listbox to display a list of items that I am selecting programmatically in a voice activated program. Is there any way to keep the selected item from being clicked on? I do want mouse over functionality, just not clicking on the actual item.
I have tried to set Focusable (does not do anything for what I want) and IsEnabled (disables mouse over)
Here is my current style:
<Style x:Key="GlyphList" TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" IsEnabled="False"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate DataType="models:SpellingGlyph">
<Label VerticalAlignment="Bottom">
<Label.Template>
<ControlTemplate>
<Grid>
<TextBlock Name="MainText" Text="{Binding Text}" VerticalAlignment="Bottom" Margin="0,0,5,0"/>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ObjectReferenceEqualityConverter}">
<Binding />
<Binding Path="SelectedItem" RelativeSource="{RelativeSource FindAncestor, AncestorType=ListBox}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter TargetName="MainText" Property="Foreground" Value="#75BAFF"/>
<Setter TargetName="MainText" Property="FontWeight" Value="SemiBold"/>
<Setter TargetName="MainText" Property="FontSize" Value="22"/>
</DataTrigger>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource MultiBooleanConverter}"> <!--SelectedItem trumps mouse over-->
<Binding ElementName="MainText" Path="IsMouseOver"/>
<Binding Path="SelectedItem" RelativeSource="{RelativeSource FindAncestor, AncestorType=ListBox}" Converter="{StaticResource InvertedNullCheckToBooleanConverter}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter TargetName="MainText" Property="Foreground" Value="#75BAFF"/>
<Setter TargetName="MainText" Property="FontWeight" Value="SemiBold"/>
<Setter TargetName="MainText" Property="FontSize" Value="22"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Label.Template>
</Label>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<ListBox PreviewMouseDown="ListBox_OnPreviewMouseDown"..
private void ListBox_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}
An alternative solution to what I accepted was to add the following in my view model:
if (_viewModelSetSelectedGlyph != value) return;
_selectedGlyph = value;
OnPropertyChanged("SelectedGlyph");
Then, set the _viewModelSetSelectedGlyph each time before actually setting the SelectedGlyph. Then, when the UI tries to set the SelectedGlyph, it would not succeed, only when the view model sets it by also setting the private variable does it succeed.
Simply set IsHitTestVisible to false
https://msdn.microsoft.com/en-us/library/system.windows.uielement.ishittestvisible(v=vs.110).aspx

Categories