<TextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}" TextWrapping="Wrap" Visibility="Collapsed" ScrollViewer.VerticalScrollBarVisibility="Visible" Grid.Row="0" x:Name="sessionNoteContent" KeyUp="SaveNote">
private void SaveNote(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
if (String.IsNullOrWhiteSpace(sessionNoteContent.Text))
{
}
else
{
Class1 note = new Class1 ()
{
time1=timeElapsed,
Notes = sessionNoteContent.Text,
StartTime = sessionStartTime,
CurrentTimestamp = DateTime.Now,
TeamId = teamId,
};
repo.InsertSessionNote(note);
sessionNoteListData.Add(note);
sessionNoteContent.Text = "";
}
sessionNoteContent.Visibility = System.Windows.Visibility.Collapsed;
sessionNotesList.Visibility = System.Windows.Visibility.Visible;
}
I have handled key up event on my texbox. It slows down the display of characters when I type . Is there some wayto handle it. Working on wpf desktop application.
Related
2nd Edit
so removing the Panel.ZIndex properties from the control template resolved this issue for me, giving me 1 drop event.
including them triggers two drop events.
can any one answer me why though?
id love to know why z index ?
Original Question :
I am trying to add a custom object (state) to a canvas called MainCanvas on the MainWindow.
I am trying to drag a state object from a wrap panel and drop it onto the canvas.
the code works but there are two items being added.
I know there are two because I can move the two item around the canvas.
I have searched existing answers and added e.Handled=true, but still adds two items
I tried using Drop event and PreviewDrop Event on MainCanvas, no difference.
Cam someone help as to how I can make it so that only 1 item gets added?
the maincanvas exists at design time
a new state is created at runtime at the drop event.
Here is the OnMouseMove handler for the state
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.LeftButton == MouseButtonState.Pressed)
{
var parent = VisualTreeHelper.GetParent(this);
if (parent as WrapPanel != null)
{
DataObject dragData = new DataObject();
dragData.SetData(DataFormats.StringFormat, this.ItemType);
DragDrop.DoDragDrop(this, dragData, DragDropEffects.Copy);
}
}
e.Handled = true;
}
Within the Code Behind I have set the following events for the canvas:
private void MainCanvas_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.Text))
e.Effects = DragDropEffects.Copy;
else
e.Effects = DragDropEffects.None;
e.Handled = true;
}
private void MainCanvas_Drop(object sender, DragEventArgs e)
{
var itemType = e.Data.GetData(typeof(string));
switch (itemType)
{
case "state":
var pos = e.GetPosition(this.MainCanvas);
State item = new State();
item.Template = (ControlTemplate)FindResource("StateViewModelControlTemplate");
this.MainCanvas.Children.Add(item);
Canvas.SetLeft(item, pos.X);
Canvas.SetTop(item, pos.Y);
e.Handled = true;
break;
default:
break;
}
e.Handled = true;
}
Finally here is the xaml for the Main Canvas
<Canvas x:Name="MainCanvas" x:Name="MainCanvas"
DockPanel.Dock="Top"
Background="#666"
Height="600"
Margin="4"
AllowDrop="True"
DragEnter="MainCanvas_DragEnter"
Drop="MainCanvas_Drop"/>
Edit:
ok so after lupus' response i went back and reconstructed everything from scratch in a separate temp project
<ControlTemplate x:Key="StateViewModelControlTemplate" TargetType="{x:Type vm:State}">
<Grid Width="100" Height="60">
<!--
If I comment out the following Thumb
the drop event will only trigger once
If i leave it in then it triggers twice
Move Thumb is derived from thumb
-->
<local:MoveThumb Panel.ZIndex="99"
x:Name="StateViewModelMoveThumb"
DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Opacity="0"/>
<Border Panel.ZIndex="98"
Margin="4"
Padding="4"
BorderBrush="white"
BorderThickness="2"
CornerRadius="5">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF59C7D4" Offset="0.5"/>
<GradientStop Color="#FF075A64" Offset="0"/>
<GradientStop Color="#FF00626E" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{TemplateBinding StateName}"/>
</Border>
</Grid>
</ControlTemplate>
By the way what I trying to do is very loosely based on the following article: https://www.codeproject.com/Articles/22952/WPF-Diagram-Designer-Part-1
I tried your code, and I cannot reproduce your issue. Changes:
private void MainCanvas_Drop(object sender, DragEventArgs e)
{
var itemType = e.Data.GetData(typeof(string));
switch (itemType)
{
var pos = e.GetPosition(this.MainCanvas);
Border item = new Border()
{
Width = 10,
Height = 10,
Background = Brushes.Red
};
//item.Template = (ControlTemplate)FindResource("StateViewModelControlTemplate");
this.MainCanvas.Children.Add(item);
...
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.LeftButton == MouseButtonState.Pressed)
{
DataObject dragData = new DataObject();
dragData.SetData(DataFormats.StringFormat, "state");
DragDrop.DoDragDrop(this, dragData, DragDropEffects.Copy);
}
e.Handled = true;
}
and in xaml,
<Window ...
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Canvas x:Name="MainCanvas" Grid.Column="0"
DockPanel.Dock="Top"
Background="#666"
Height="600"
Margin="4"
AllowDrop="True"
DragEnter="MainCanvas_DragEnter"
Drop="MainCanvas_Drop"/>
<Grid Grid.Column="1" Background="Red"/>
</Grid>
Have not touch the other piece of code. Everytime I drag from the Grid to the Canvas, I get one Border item at the drop position.
So maybe the problem is not in the code you posted, but in the code left out or commented.
So I eventually got this working and here is how
Here is the front end xaml on the mainwindow
<cc:DiagramCanvas x:Name="MainCanvas"
DockPanel.Dock="Top"
Margin="0"
MinHeight="450"
AllowDrop="True" Background="White">
</cc:DiagramCanvas>
Here is the custom canvas object and the drag drop handler
public class DiagramCanvas : Canvas
{
public DiagramCanvas()
{
this.Drop += DoDrop;
this.DragEnter += MainCanvas_DragEnter;
}
readonly MainWindow mainWin = (MainWindow)Application.Current.MainWindow;
#region works dont touch
public void DoDrop(object sender, DragEventArgs e)
{
var mainWin = (MainWindow)App.Current.MainWindow;
DragDropHandler<StateVM>.Instance.Drop(sender, e);
}
private void MainCanvas_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.Text))
e.Effects = DragDropEffects.Copy;
else
e.Effects = DragDropEffects.None;
e.Handled = true;
}
#endregion
//other code here
}
Here is the drag drop handler singleton
public sealed class DragDropHandler<T> where T : Control
{
#region singleton
private static DragDropHandler<T> instance = null;
private static readonly object padlock = new object();
DragDropHandler()
{
}
public static DragDropHandler<T> Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new DragDropHandler<T>();
}
return instance;
}
}
private set
{
instance = value;
}
}
#endregion
public static bool IsDragging { get; set; }
public static WrapPanel AllowedDragSource { get; set; }
readonly MainWindow mainWin = (MainWindow)Application.Current.MainWindow;
public static void CreateInstance(WrapPanel allowedSource)
{
if (DragDropHandler<T>.IsDragging == false)
{
instance = new DragDropHandler<T>();
DragDropHandler<T>.AllowedDragSource = allowedSource;
}
}
public void Drag(object sender, MouseEventArgs e)
{
if (sender as T == null
|| mainWin.Radio_EditStates.IsChecked == false
|| e.LeftButton != MouseButtonState.Pressed
|| IsDragging == true)
{
e.Handled = true;
return;
}
var item = (T)sender;
if (Control.ReferenceEquals(item.Parent, AllowedDragSource) == false)
{
e.Handled = true;
return;
}
IsDragging = true;
DragDrop.DoDragDrop(((StateVM)sender), new DataObject(((StateVM)sender)), DragDropEffects.Copy);
IsDragging = false;
e.Handled = true;
}
public void Drop(object sender, DragEventArgs e)
{
var mainWin = (MainWindow)App.Current.MainWindow;
if (IsDragging)
{
//TODO: Switch here to handle different shapes
var pos = e.GetPosition(mainWin.MainCanvas);
//
Canvas.SetLeft(item, pos.X.RoundDownTo10());
Canvas.SetTop(item, pos.Y.RoundDownTo10());
//update main win observalbe collections to inclue the item dropped
IsDragging = false;
e.Handled = true;
DestroyInstance();
}
}
private static void DestroyInstance()
{
DragDropHandler<T>.Instance = null;
}
}
Here is the on mouse move code for the item you are dragging
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.LeftButton == MouseButtonState.Pressed && Control.ReferenceEquals(this.Parent, mainWin.libraryContainer))
{
DragDropHandler<StateVM>.CreateInstance(mainWin.libraryContainer);
if (DragDropHandler<StateVM>.Instance != null)
{
DragDropHandler<StateVM>.Instance.Drag(this, e);
}
}
}
I am developing a WPF application in which I would like to use the default DatePicker control. I also want to let the user insert manually the date, but I want to prevent the separator ("/") to be deleted by the user. (more precisely, even if the DatePicker's textbox is "empty", the separators should be still there)
Can this be done without creating a new Custom Control? Thank you!
I have made the following sample:
<DatePicker HorizontalAlignment="Left" Margin="132,118,0,0" VerticalAlignment="Top" Width="204" Name="DatePicker"
SelectedDateChanged="DatePicker_OnSelectedDateChanged">
<DatePicker.Resources>
<Style TargetType="{x:Type DatePickerTextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox x:Name="TextBox1" Grid.Column="0"
Style="{StaticResource DatePickerTextBoxStyle}"
TextChanged="OnTextboxTextChanged1" PreviewTextInput="OnPreviewTextInput1"/>
<Label Grid.Column="1" Content="/" FontSize="18" HorizontalAlignment="Center"/>
<TextBox x:Name="TextBox2" Grid.Column="2" Style="{StaticResource DatePickerTextBoxStyle}"
TextChanged="OnTextboxTextChanged2" PreviewTextInput="OnPreviewTextInput2"/>
<Label Grid.Column="3" Content="/" FontSize="18" HorizontalAlignment="Center"/>
<TextBox x:Name="TextBox3" Grid.Column="4" Style="{StaticResource DatePickerTextBoxStyle}"
TextChanged="OnTextboxTextChanged3" PreviewTextInput="OnPreviewTextInput3"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DatePicker.Resources>
</DatePicker>
And in your codebehind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private static void TrimText(TextBox textbox)
{
textbox.Text = textbox.Text.Trim();
if (textbox.Text.Length == 2) textbox.Text = textbox.Text.PadLeft(2, '0');
textbox.CaretIndex = textbox.Text.Length;
}
private void OnTextboxTextChanged1(object sender, TextChangedEventArgs e)
{
var textbox = sender as TextBox;
if (textbox == null) return;
TrimText(textbox);
if (textbox.Text.Length != 2) return;
var datePickerTxtBox = DatePicker.Template.FindName("PART_TextBox", DatePicker) as DatePickerTextBox;
var secondTxtbox = datePickerTxtBox.Template.FindName("TextBox2", datePickerTxtBox) as TextBox;
secondTxtbox.Focus();
}
private void OnTextboxTextChanged2(object sender, TextChangedEventArgs e)
{
var textbox = sender as TextBox;
if (textbox == null) return;
TrimText(textbox);
if (textbox.Text.Length != 2) return;
var datePickerTxtBox = DatePicker.Template.FindName("PART_TextBox", DatePicker) as DatePickerTextBox;
var thirdTxtbox = datePickerTxtBox.Template.FindName("TextBox3", datePickerTxtBox) as TextBox;
thirdTxtbox.Focus();
}
private void OnTextboxTextChanged3(object sender, TextChangedEventArgs e)
{
var textbox = sender as TextBox;
if (textbox == null) return;
TrimText(textbox);
if (textbox.Text.Length != 2) return;
var datePickerTxtBox = DatePicker.Template.FindName("PART_TextBox", DatePicker) as DatePickerTextBox;
var firstTxtbox = datePickerTxtBox.Template.FindName("TextBox1", datePickerTxtBox) as TextBox;
var secondTxtbox = datePickerTxtBox.Template.FindName("TextBox2", datePickerTxtBox) as TextBox;
DateTime date;
var text = firstTxtbox.Text + secondTxtbox.Text + textbox.Text;
if (!DateTime.TryParseExact(text,"ddMMyy",null,DateTimeStyles.None, out date))
firstTxtbox.Text = secondTxtbox.Text = textbox.Text = string.Empty;
else DatePicker.DisplayDate = date;
}
private void OnPreviewTextInput1(object sender, TextCompositionEventArgs e)
{
e.Handled = !(new Regex("[0-9]+").IsMatch(e.Text));
}
private void OnPreviewTextInput2(object sender, TextCompositionEventArgs e)
{
e.Handled = !(new Regex("[0-9]+").IsMatch(e.Text));
}
private void OnPreviewTextInput3(object sender, TextCompositionEventArgs e)
{
e.Handled = !(new Regex("[0-9]+").IsMatch(e.Text));
}
private void DatePicker_OnSelectedDateChanged(object sender, SelectionChangedEventArgs e)
{
var newDate = DateTime.Parse(e.Source.ToString());
DatePicker.DisplayDate = newDate;
var datePickerTxtBox = DatePicker.Template.FindName("PART_TextBox", DatePicker) as DatePickerTextBox;
var firstTxtbox = datePickerTxtBox.Template.FindName("TextBox1", datePickerTxtBox) as TextBox;
var secondTxtbox = datePickerTxtBox.Template.FindName("TextBox2", datePickerTxtBox) as TextBox;
var thirdTxtbox = datePickerTxtBox.Template.FindName("TextBox3", datePickerTxtBox) as TextBox;
firstTxtbox.Text = DatePicker.DisplayDate.ToString("dd");
secondTxtbox.Text = DatePicker.DisplayDate.ToString("MM");
thirdTxtbox.Text = DatePicker.DisplayDate.ToString("yy");
}
}
This is just a sample of how you may work around this. It is lacking of some validation, mainly in the inner textboxes used to hold year, month and day values. Nevertheless it is something as you possibly want
EDIT>>> Also the codebehind must be improved for production as I have lot of repeated code. But this is just an example.
EDIT2>>>> This is how it looks:
please have look on my code,
this become an infinite loop while calling lostfocus event of combo box
i need some data from database and user can select data only form list with typing options.
mainwindow.xaml
<Grid>
<TextBox x:Name="txt1" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120" Margin="112,10,0,0"/>
<ComboBox x:Name="cmb" GotFocus="cmbgotfocus" LostKeyboardFocus="cmblost" KeyDown="cmbkeydown" IsEditable="True" HorizontalAlignment="Left" VerticalAlignment="Top" Width="238" Margin="112,50,0,0" />
</Grid>
Class
private void cmbkeydown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return || e.Key == Key.Escape)
{
cmb.IsDropDownOpen = false;
}
else
{
cmb.IsDropDownOpen = true;
}
}
private void cmblost(object sender, RoutedEventArgs e)
{
if (cmb.SelectedIndex < 0 && cmb.Text!="" )
{
MessageBox.Show("Please select a valid data from list only", "Warning");
cmb.Focus();
}
}
If I got you correctly, you want user to type some text in the ComboBox, and if user's entry doesn't match any item, focus should remain on the TextBox present in the ComboBox.
<ComboBox x:Name="Cmb1" IsEditable="True"
Control.PreviewLostKeyboardFocus="Control_PreviewLostKeyboardFocus" ...>
Handler code :
private void Control_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (!(e.OriginalSource is TextBox)) return;
TextBox tb = (TextBox)e.OriginalSource;
if (Cmb1.SelectedIndex < 0)
{
Cmb1.Text = "";
e.Handled = true;
}
}
Please tell if this solves your issue.
How to validate RichTextbox in WPF? I want to validate the text for email and email separator i.e. emails should be entered with a semicolon.
Xaml:
<StackPanel Orientation="Horizontal">
<RichTextBox x:Name="txtEmail" Style="{StaticResource ContentRichTextBox}"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="0,0,10,0">
<FlowDocument>
<Paragraph LineHeight="5"></Paragraph>
</FlowDocument >
</RichTextBox>
</StackPanel>
<StackPanel HorizontalAlignment="Center">
<TextBlock x:Name="txterrormessage" Width="300" Foreground="#FFE5572C" FontSize="14" Visibility="Hidden" TextWrapping="Wrap"></TextBlock>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Margin="60,0,0,0">
<Button x:Name="BtnEmail" Style="{StaticResource ShortButtonStyle}" Content="NEXT" Margin="10" Command="{Binding CommandChanged}" CommandParameter="PROJECTS" Click="BtnEmail_Click"/>
</StackPanel>
This is my code:
private void BtnEmail_Click(object sender, RoutedEventArgs e)
{
string richText = new TextRange(txtEmail.Document.ContentStart, txtEmail.Document.ContentEnd).Text;
if (!Regex.IsMatch(richText, #"^[a-zA-Z][\w\.-]*[a-zA-Z0-9]#[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$"))
{
txterrormessage.Text = "Enter a valid email";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
}
else
{
txterrormessage.Visibility = System.Windows.Visibility.Hidden;
}
if (!Regex.IsMatch(richText, #"^((\w+([-+.]\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*)\s*[;]{0,1}\s*)+$"))
{
txterrormessage.Text = "Separate emails with ;";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
}
}
The code doesnt seem to work....How to validate?
Thanks
The simplest way I've seen to do this is
private void OnVerifyEmail()
{
var recipients = richText.Split(';', StringSplitOptions.RemoveEmptyEntries);
var validator = new System.ComponentModel.DataAnnotations.EmailAddressAttribute();
foreach (var recipient in recipients)
{
var isValid = validator.IsValid(recipient.Trim());
if(!isValid)
{
// do your thing here
}
}
}
format your richText before validation:
richText = Regex.Replace(richText, #"(\n|\r)", "", RegexOptions.Multiline);
Edit:
This is the whole method and probably what you're looking for:
private void BtnEmail_Click(object sender, RoutedEventArgs e)
{
string richText = new TextRange(txtEmail.Document.ContentStart, txtEmail.Document.ContentEnd).Text;
richText = Regex.Replace(richText, #"(\n|\r)", "", RegexOptions.Multiline);
richText = Regex.Replace(richText, #"( ;|; )", ";", RegexOptions.Multiline);
txterrormessage.Visibility = System.Windows.Visibility.Hidden;
if (!Regex.IsMatch(richText, #"^[\W]*([\w+\-.%]+#[\w\-.]+\.[A-Za-z]{2,4}[\W]*,{1}[\W]*)*([\w+\-.%]+#[\w\-.]+\.[A-Za-z]{2,4})[\W]*$"))
{
string[] emails = Regex.Split(richText, ";", RegexOptions.Multiline);
foreach (string item in emails)
{
if (string.IsNullOrEmpty(item))
continue;
if (!Regex.IsMatch(item, #"^\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*$"))
{
txterrormessage.Text = item + " is not a valid email address";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
break;
}
}
if (string.IsNullOrEmpty(txterrormessage.Text))
{
txterrormessage.Text = "Separate emails with ; ";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
}
}
}
I am writing a Silverlight for Windows Phone 7.5 app.
I want to reference the ContextMenu inside my LongListSelector because I want to set .IsOpen to false as soon as the ContextMenu Click event is called. My thought was that this should happen automatically but it does not.
One of my MenuItem's sets the visibility of a <Grid> from collapsed to visible which mimics a PopUp. Whilst the code executes fine and the Visibility does indeed change. The UI of the app does not show the Grid unless the ContextMenu closes.
My XAML of the LongListSelector which contains a ContextMenu called Menu that I wish to reference in the ContextMenuItem Click event.
<toolkit:LongListSelector x:Name="moviesLongList" Background="Transparent" IsFlatList="False" GroupHeaderTemplate="{StaticResource GroupHeaderTemplate}" GroupItemTemplate="{StaticResource GroupItemTemplate}" SelectionChanged="moviesLongList_SelectionChanged" GroupViewClosing="moviesLongList_GroupViewClosing" GroupViewOpened="moviesLongList_GroupViewOpened">
<toolkit:LongListSelector.GroupItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel/>
</ItemsPanelTemplate>
</toolkit:LongListSelector.GroupItemsPanel>
<toolkit:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Height="91" Margin="20,0,0,20" Orientation="Horizontal">
<toolkit:ContextMenuService.ContextMenu >
<toolkit:ContextMenu x:Name="Menu" Opened="ContextMenu_Opened" Loaded="Menu_Loaded" Unloaded="Menu_Unloaded">
<toolkit:ContextMenu.ItemTemplate>
<DataTemplate>
<toolkit:MenuItem Header="{Binding}" Click="ContextMenuButton_Click" LostFocus="MenuItem_LostFocus" />
</DataTemplate>
</toolkit:ContextMenu.ItemTemplate>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<Border HorizontalAlignment="Left" Width="61" Height="91" Background="{Binding ID, Converter={StaticResource ThumbImageConvert}}" />
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" Width="395">
<TextBlock x:Name="titleTextBox" Text="{Binding Title, Converter={StaticResource TitleConvert}}" Margin="6,0,6,0" d:LayoutOverrides="Width" FontSize="{StaticResource PhoneFontSizeLarge}" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<TextBlock x:Name="yearTextBox" Text="{Binding Year}" Margin="12,0,0,0" HorizontalAlignment="Left" FontSize="{StaticResource PhoneFontSizeMedium}" Foreground="{StaticResource PhoneSubtleBrush}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</toolkit:LongListSelector.ItemTemplate>
</toolkit:LongListSelector>
My code behind ContextMenuItem Click event
private void ContextMenuButton_Click(object sender, RoutedEventArgs e)
{
//
// This is where I want to set Menu.IsOpen = false to close the ContextMenu.
//
if ((sender as MenuItem).Header.ToString() == "lend movie")
{
DisableAppBarIcons();
LendPopUpOverlay.Visibility = System.Windows.Visibility.Visible;
}
if ((sender as MenuItem).Header.ToString() == "return to collection")
{
... Do stuff
}
if ((sender as MenuItem).Header.ToString() == "add to boxset")
{
... Do stuff
}
if ((sender as MenuItem).Header.ToString() == "delete")
{
... Do stuff
}
}
I set the ItemSource of the ContextMenu in the ContextMenu_Opened event. The fields are both of type List<String>.
private void ContextMenu_Opened(object sender, RoutedEventArgs e)
{
LentMovieObj = (sender as ContextMenu).DataContext as Movies;
if (LentMovieObj.IsLent)
{
(sender as ContextMenu).ItemsSource = menuItemsReturn;
}
else
{
(sender as ContextMenu).ItemsSource = menuItemsLendOut;
}
}
Not sure why the ContextMenu is not closing, but here are two solutions. The first is to get the parent of the MenuItem.
private T GetParentOfType<T>(DependencyObject obj) where T : class
{
if (obj == null) return null;
var parent = VisualTreeHelper.GetParent(obj);
while (parent != null)
{
if (parent is T) return parent as T;
parent = VisualTreeHelper.GetParent(parent);
}
return null;
}
Then get the Menu from your click handler
var menu = GetParentOfType<ContextMenu>(sender as MenuItem);
menu.IsOpen = false;
The second solution is to bind IsOpen to a backing viewmodel
<toolkit:ContextMenuService.ContextMenu >
<toolkit:ContextMenu x:Name="Menu" Opened="ContextMenu_Opened" Loaded="Menu_Loaded" Unloaded="Menu_Unloaded" IsOpen="{Binding IsOpen}" ItemsSource="{Binding Items}">
<toolkit:ContextMenu.ItemTemplate>
<DataTemplate>
<toolkit:MenuItem Header="{Binding}" Click="ContextMenuButton_Click" LostFocus="MenuItem_LostFocus" />
</DataTemplate>
</toolkit:ContextMenu.ItemTemplate>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
Change your open event:
private void ContextMenu_Opened(object sender, RoutedEventArgs e)
{
LentMovieObj = (sender as ContextMenu).DataContext as Movies;
if (LentMovieObj.IsLent)
{
(sender as ContextMenu).DataContext = new ContextMenuViewModel(menuItemsReturn);
}
else
{
(sender as ContextMenu).DataContext = ContextMenuViewModel(menuItemsLendOut);
}
}
Then a viewmodel
public class ContextMenuViewModel : INotifyPropertyChanged
{
private bool _isOpen = true;
public ContextMenuViewModel(IEnumerable<string> items)
{
Items = items;
}
public event PropertyChangedEventHandler PropertyChanged;
public bool IsOpen
{
get { return _isOpen; }
set { _isOpen = value; OnPropertyChanged("IsOpen"); }
}
public IEnumerable<String> Items { get; set; }
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then set the IsOpen property of your ViewModel to false;