I have a canvas and when a user right clicks on it, a context menu appears. I also have a checkbox, and when that box is checked, I DON'T want the context menu to appear. The reason for this is that, when the checkbox is checked, the first two right clicks from the user will drop ellipses at the two points of the right clicks. Right now though, the context menu will popup on those two right clicks. Here's the relative code:
<Window x:Class="Testproj.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Testproj"
xmlns:localConverters="clr-namespace:Testproj"
x:Name="this"
Height="650" Width="1091"
Loaded="this_Loaded"
Closing="this_Closing">
<Window.Resources>
<local:BoolToVisibilityConverter x:Key="converter"/>
</Window.Resources>
<Grid Height="Auto">
<Grid.Resources>
<local:NullToVisibilityConverter x:Key="nullToVisibilityConverter" />
</Grid.Resources>
<Grid VerticalAlignment="Top">
<DockPanel>
<CheckBox x:Name="scaleBox" Content="Scale" IsChecked="False" Checked="scaleischecked"/>
</Menu>
</DockPanel>
</Grid>
<Viewbox Margin="0,23,0,157" x:Name="viewbox1" ClipToBounds="True">
<Canvas Margin="0,21,0,12" x:Name="canvas1" ClipToBounds="True" RenderOptions.BitmapScalingMode="HighQuality" MouseWheel="Canvas_Zoom" MouseRightButtonDown="get_MousePosition" HorizontalAlignment="Left" Width="3138" Height="1260">
<Canvas.RenderTransform>
<MatrixTransform x:Name="mt"/>
</Canvas.RenderTransform>
<Canvas.ContextMenu>
<ContextMenu Name="nodeContextMenu" Visibility="{StaticResource converter}" >
<MenuItem x:Name="test1" IsCheckable="False" Header="test1" Click="WaypointMenuItem_Click" >
</MenuItem>
<MenuItem x:Name="test2" IsCheckable="False" Header="test2" Click="KnownObjectMenuItem_Click" >
</MenuItem>
</ContextMenu>
</Canvas.ContextMenu>
</Canvas>
</Viewbox>
</Grid>
</Window>
and the code behind for the right-click on canvas:
private void get_MousePosition(object sender, MouseButtonEventArgs e)
{
if (scaleBox.IsChecked == true)
{
get_points(sender, e);
}
}
I've tried messing around with the isOpen property of the context-menu, but it always open on right click regardless if it's set to true or false.
Attempt at converter below. If this is correct, what is the proper way to bind the checkbox and contextmenu using this?
namespace Testproj
{
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility visibility = Visibility.Collapsed;
if (value != null)
{
visibility = (bool)value ? Visibility.Collapsed : Visibility.Visible;
}
return visibility;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
I would implement a ValueConverter or MultiValueConverter and then bind to the checkbox using the converter to dictate the state (i.e. enabled/disabled) of the contextmenu.
<Canvas.ContextMenu>
<ContextMenu Name="contextmenu1" Visibility="{Binding ElementName=scaleBox, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" >
<MenuItem x:Name="item1" IsCheckable="False" Header="item2" />
<MenuItem x:Name="item2" IsCheckable="False" Header="item1" />
</ContextMenu>
Here's the way I figured out how to do it since I couldn't get the converter to work properly:
Set the ContextMenuService.IsEnabled property to false in the canvas. Then, in the code behind, set nodeContextMenu.IsOpen = true when the scalebox is not checked. This seems to do the trick.
<Canvas Margin="0,21,0,12" x:Name="canvas1" ContextMenuService.IsEnabled="False" />
if (scaleBox.IsChecked == true)
{
get_Scaling(sender, e);
}
else
{
nodeContextMenu.IsOpen = true;
}
Related
I want to open a window on button click from another window and go to specific tab in that window. Let's say new window (tabsWindow) has 3 tabs - tab1, tab2 and tab3 and I have 3 buttons on main window (buttonsWindow) btn1, btn2 and btn3. All three tabs are on one window and all three buttons are on another window. On click of btn1, tab1 should be opened from tabsWindow. On btn2_Click, tab2 should open. I have heard of RoutedCommand but not good in it. Suggest me any other possible or simpler way.
I made a toy sample by using MVVM pattern.
You can see full source in GitHub.
ButtonsWindow.xaml
I used Command and handed over the Button object to the CommandParameter to specify the SelectedIndex of TabControl using the Content of the Button.
<UniformGrid Columns="3">
<Button Margin="50" Content="1" Command="{Binding BtnClick}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
<Button Margin="50" Content="2" Command="{Binding BtnClick}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
<Button Margin="50" Content="3" Command="{Binding BtnClick}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
</UniformGrid>
TabsWindow.xaml
<Grid>
<TabControl x:Name="tab" Width="300" Height="300">
<TabItem Header="Tab1">
<TextBlock TextAlignment="Center" VerticalAlignment="Center" Text="Tab1" FontSize="20" />
</TabItem>
<TabItem Header="Tab2">
<TextBlock TextAlignment="Center" VerticalAlignment="Center" Text="Tab2" FontSize="20" />
</TabItem>
<TabItem Header="Tab3">
<TextBlock TextAlignment="Center" VerticalAlignment="Center" Text="Tab3" FontSize="20" />
</TabItem>
</TabControl>
</Grid>
TabsWindow.xaml.cs
public partial class TabsWindow : Window
{
public TabsWindow()
{
InitializeComponent();
}
internal void SetTab(string content)
{
tab.SelectedIndex = int.Parse(content) - 1;
}
}
MainViewModel.cs
public class MainViewModel
{
private TabsWindow win;
public ICommand BtnClick { get; set; }
public MainViewModel()
{
BtnClick = new RelayCommand<object>(Click);
}
private void Click(object obj)
{
if (obj is Button btn)
{
if (win is null || !win.IsVisible)
{
win = new TabsWindow();
win.Show();
}
win.SetTab(btn.Content.ToString());
}
}
}
When Button is clicked, TabsWindow is activated and moves to a specific tab according to the Content of the Button. Also, if TabsWindow is already activated, tab can be moved without showing a new TabsWindow.
I have a popup that opens when we click on the toggle button. I have written a converter such that popup closes whenever we click on toggle button again or when we click anywhere other than the popup.
the XAML code is this
<ToggleButton x:Name="m_XYProfileBtn" Content="Profiles" Height="24" Style="{DynamicResource StatusFlatToggle}"
Visibility="{Binding IsToggleStatusVisibility}" IsChecked="{Binding IsPopUpOpen}" />
<Popup Margin="5" Name="ProfilePopup" HorizontalAlignment="Center" VerticalAlignment="Center"
Width="400" Height="500" AllowsTransparency="True"
IsOpen="{Binding IsPopUpOpen}"
Focusable="False"
PopupAnimation="Slide"
Placement="Top"
VerticalOffset="-4"
HorizontalOffset="2"
>
<Popup.StaysOpen>
<MultiBinding Converter="{StaticResource MultiBinding_StayOpen}">
<Binding ElementName="m_XYProfileBtn" Path="IsMouseOver"/>
<Binding ElementName="Pin" Path="IsChecked" />
</MultiBinding>
</Popup.StaysOpen>
<Border BorderThickness="2" BorderBrush="Gray" Background="White" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.05*" />
<RowDefinition Height="0.95*" />
</Grid.RowDefinitions>
<CheckBox Name="Pin" Content="Pin" Style ="{StaticResource PinBox}" HorizontalAlignment="Right"/>
<Grid Name="xProfileGrid" Grid.Row="1" />
</Grid>
</Border>
and the C# converter code is this
public class MultiBinding_StayOpen : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool toggleIsMouseOver;
bool pinIsChecked;
//System.Threading.Thread.Sleep(3000);
toggleIsMouseOver = System.Convert.ToBoolean(values[0]);
pinIsChecked = System.Convert.ToBoolean(values[1]);
if (pinIsChecked == true)
return true;
else if (toggleIsMouseOver == true)
return true;
else if (toggleIsMouseOver == false)
{
System.Threading.Thread.Sleep(300);
return false;
}
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This works fine but the first time you click on the toggle button, popup opens up again in place of closing. After that it works perfectly. Any suggestion how I can fix this?
I'm trying to achieve something that sounds pretty simple, in WPF, but just can't get around doing it.
I have a ScrollViewer which contains two GroupBoxes. First one has it's height set to a fixed value, second one would take what's left of the window but have a MinHeight. Each GroupBox contains a DataGrid.
What i'm trying to do is :
The second groupbox should be sized to what's left of the Window and the DataGrid inside of it should be sized to fill the group box and have it's own scrollbar if rows can't all be shown. A scrollbar should appear in the window if i resize the window to be less than GroupBox1.Height+GroupBox2.MinHeight.
The behavior i get now is, the DataGrid in the second groupbox's height grows with the number of lines, thus expanding the Groupbox and having the Scrollviewer's scrollbar show up.
I came up with a little demo-app to show this behavior
WPF:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow"
Height="400"
Width="500">
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="150" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<GroupBox Header="test1"
Grid.Row="0">
<DataGrid ItemsSource="{Binding Colors}">
</DataGrid>
</GroupBox>
<GroupBox Header="test2"
Grid.Row="1"
MinHeight="50">
<DataGrid ItemsSource="{Binding Colors}">
</DataGrid>
</GroupBox>
</Grid>
</ScrollViewer>
</Grid>
C#
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Colors = new List<Color>();
for (int i = 1; i < 51; i++)
{
byte b = (byte)(i * 5);
Colors.Add(Color.FromRgb(b,b,b));
}
}
private List<Color> _colors;
public List<Color> Colors
{
get
{
return _colors;
}
set
{
_colors = value;
}
}
}
}
What i'm getting :
What i would want (sorry for the bad photo-manipulation skills) :
Unless, as specified earlier, i resize the window to be smaller than the sum of the group1's fixed size and group2's min size, in which case i want the window's scrollbar.
In which case, i would want it to look like this : (again a mock-up, not an actual screenshot)
Mind you, the example is pretty simple but the window i'm trying to do it in is much more complex and it makes more sense to have a vertical scrollbar than it does in this example.
Thanks!
You can simply bind the MaxHeight property of the second GroupBox to the ActualHeight of the container of the ScrollViewer minus the first GroupBox.
Complete example (excluding the code behind which is same as yours.):
<Window.Resources>
<wpfApp1:SubtractConverter x:Key="SubtractConverter"/>
</Window.Resources>
<Grid Name="Root">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="150" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GroupBox
Name="Test1"
Header="test1"
Grid.Row="0">
<DataGrid ItemsSource="{Binding Colors}"/>
</GroupBox>
<GroupBox
Header="test2"
Grid.Row="1"
MinHeight="250">
<DataGrid ItemsSource="{Binding Colors}"/>
<GroupBox.MaxHeight>
<MultiBinding Converter="{StaticResource SubtractConverter}">
<Binding Path="ActualHeight" ElementName="Root"/>
<Binding Path="ActualHeight" ElementName="Test1"/>
</MultiBinding>
</GroupBox.MaxHeight>
</GroupBox>
</Grid>
</ScrollViewer>
</Grid>
public class SubtractConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double[] doubles = values.Cast<double>().ToArray();
double result = doubles[0];
for (int i = 1; i < doubles.Length; i++)
{
result -= doubles[i];
}
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I don't know if this would be the easiest solution for your problem but you could do something along this line:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow"
Height="400"
Width="500">
<Window.Resources>
<local:HeightConverter x:Key="HeightConverter"/>
</Window.Resources>
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto" x:Name="MainView">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="150" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<GroupBox Header="test1"
Grid.Row="0">
<DataGrid ItemsSource="{Binding Colors}">
</DataGrid>
</GroupBox>
<GroupBox Header="test2"
Grid.Row="1"
x:Name="grpBox2"
MinHeight="50">
<GroupBox.Height>
<MultiBinding Converter="{StaticResource HeightConverter}" ConverterParameter="150">
<Binding Path="ActualHeight" ElementName="MainView" />
<Binding Path="MinHeight" ElementName="grpBox2" />
</MultiBinding>
</GroupBox.Height>
<DataGrid ItemsSource="{Binding Colors}">
</DataGrid>
</GroupBox>
</Grid>
</ScrollViewer>
</Grid>
And for the converter something like this:
public class HeightConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || parameter == null || values[0] == null || values[1] == null)
{
return null;
}
var currentWindowHeight = double.Parse(values[0].ToString());
var currentMinHeight = double.Parse(values[1].ToString());
var currentTopWindowHeight = double.Parse(parameter.ToString());
var newHeight = currentWindowHeight - currentTopWindowHeight;
if (newHeight < currentMinHeight)
newHeight = currentMinHeight;
return newHeight;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I got a ResourceDictionary in my WPF project. The entire project is meant to be running in background. In ResourceDictionary there is a context menu, code is here;
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Commands="clr-namespace:_365Drive.Office365.NotificationManager"
xmlns:tb="http://www.hardcodet.net/taskbar"
xmlns:local="clr-namespace:_365Drive.Office365.NotificationManager">
<LinearGradientBrush x:Key="MenuBackground" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="White" Offset="1" />
<GradientStop Color="White" Offset="0.259" />
</LinearGradientBrush>
<ContextMenu x:Shared="false" x:Key="SysTrayMenu" Name="contextMenu">
<MenuItem Header="Sign in" Command="{Binding ShowAuthForm}" CommandParameter="{Binding}" />
<MenuItem Header="Prompt MFA now" Name="MFA" Command="{Binding ClearMFACacheCommand}" CommandParameter="{Binding}" Visibility="{Binding MFAVisibility}" />
<MenuItem Header="Sign out" Command="{Binding SignOutApplicationCommand}" CommandParameter="{Binding}" />
<Separator />
<MenuItem Header="Update Drive Mappings" Command="{Binding RefreshSettingsCommand}" CommandParameter="{Binding}" />
<Separator />
<MenuItem Header="Exit" Command="{Binding ExitApplicationCommand}" />
</ContextMenu>
</ResourceDictionary>
The code behind of it is as follows (Snippet of code only)
public class NotifyIconViewModel
{
private Visibility visibility = Visibility.Collapsed;
public Visibility MFAVisibility
{
get
{
return ((1) == null ? Visibility.Collapsed : Visibility.Visible);
}
set
{
visibility = value;
}
}
}
There is other code in the code behind but that is irrelevant for this question.
I want to update the above property value from other class in the same project but I am not sure how can I get the instance of that class which is bound with the contextMenu.
And if I do try to set the MenuItem Visibility using code like below, it doesnt work;
public static void DisableMFAMenuitem()
{
System.Windows.Controls.ContextMenu ctxMenu = (System.Windows.Controls.ContextMenu)System.Windows.Application.Current.FindResource("SysTrayMenu");
System.Windows.Controls.ItemCollection items = ctxMenu.Items;
System.Windows.Controls.MenuItem itemtobeRemoved = null;
foreach (var item in items)
{
if (item.GetType() == typeof(System.Windows.Controls.MenuItem))
{
// do your work with the item
if (((System.Windows.Controls.MenuItem)item).Name == "MFA")
{
((System.Windows.Controls.MenuItem)item).Visibility = Visibility.Collapsed; // OR WHATEVER really
}
}
}
}
I think I am doing some small mistake in understanding, can anyone help here please?
Comment from Grx70 above helped.
I'm not sure I understand what you're trying to achieve here, but from
the first glimpse my guess would be that the troublemaker is the
x:Shared="False" attribute set on your resource, which means that each
FindResource("SysTrayMenu") call will yield a new ContextMenu instance
(so you're not modifying any pre-existing instance).
i have textbox with button. my deire is, if textbox is empty, the button should be deactivated (means 0.5 opacity). if user enters something in the textbox, the button should be set to visible. like if users clicked the button, again it should be deactivated unless it done the work for what purpose it has been clicked ?
Any ideas, how to do that ?
My CODE;
<Button Content="Go !" Foreground="White" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="283,-3,0,0" Width="161" Name="searchbutton" Click="search" Height="78" BorderBrush="Transparent"/>
You could use a value converter to convert the text of the TextBox to the Opacity value of the button:
<TextBox x:Name="txtBox" />
<Button Opacity="{Binding Text, ElementName=txtBox, Converter={StaticResource textToOpacityConverter}}" Content="Go !" Foreground="White" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="283,-3,0,0" Width="161" Name="searchbutton" Click="search" Height="78" BorderBrush="Transparent" />
This is the value converter:
public class TextToOpacityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (string.IsNullOrEmpty(value as string))
{
return 0.5;
}
return 1;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
If you need more info about ValueConverter you can see here http://www.c-sharpcorner.com/uploadfile/dpatra/value-converter-in-wpf-part-i/
This control Template can be used to set the opacity of button if Textbox text is empty.
<ControlTemplate x:Key="myButtonTemplate" TargetType="{x:Type ContentControl}">
<StackPanel Orientation="Horizontal">
<TextBox Width="100" x:Name="searchTxt" Text="{Binding}"></TextBox>
<Button x:Name="myButton">Search</Button>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger SourceName="searchTxt" Property="Text" Value="">
<Setter TargetName="myButton" Property="Opacity" Value="0.2"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
control implementing the template
<ContentControl Template="{StaticResource myButtonTemplate}">
Thanks
Like as said Emanuele use just IsEnable property will setcolor of button to grey :)
You can use TextChanged event and then use Length on the text inside it. For button just use Click event and set Opacity to your desired value.