WPF Combobox lostfocus event become an Infinite lopp - wpf

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.

Related

Combobox with textbox inside it in WPF

I created a combo box that has a text box inside. Now I want it to filter the corresponding items for me when I type in the text box. How can I do this?
<ComboBox Grid.Column="2" Grid.Row="2" Margin="0,10,10,27" Name="comboBox3" >
<TextBox Name="text2" Width="90" TextChanged="text2_TextChanged"
Style="{StaticResource ComboBoxEditableTextBox}"></TextBox>
<ComboBoxItem >salam</ComboBoxItem >
<ComboBoxItem >khobi</ComboBoxItem >
<ComboBoxItem>سلام</ComboBoxItem>
<ComboBoxItem>خوبی</ComboBoxItem>
<ComboBoxItem>عرض ادب</ComboBoxItem>
<ComboBoxItem>سپاسگذارم</ComboBoxItem>
<ComboBoxItem>مرسی</ComboBoxItem>
</ComboBox>
Code:
private void text2_TextChanged(object sender, TextChangedEventArgs e)
{
int c = comboBox3.Items.Count;
MessageBox.Show(c.ToString());
object[] st = new object[c];
List<string> listsource = new List<string>();
for (int i = 1; i <= 8; i++)
{
MessageBox.Show(comboBox3.Items[i].ToString());
// listsource.Add(ComboBox3.Items.IndexOf(ComboBox3.SelectedItem));
// listsource.Add(comboBox3.Items[i].ToString());
// foreach (ComboBoxItem cbi in comboBox3.Items)
}
comboBox2.ItemsSource = listsource;
}
No need to add a TextBox. If you set IsEditable="True" on the ComboBox then you can type a search term directly in the ComboBox.
To filter items handle KeyUp event on the ComboBox. IsDropDownOpen and StaysOpenOnEdit are set to true for testing purpose.
<ComboBox Grid.Column="2" Grid.Row="2" Margin="0,10,10,27" Name="comboBox3"
IsEditable="True" IsDropDownOpen="True" StaysOpenOnEdit="True" KeyUp="comboBox3_KeyUp" >
<ComboBoxItem >salam</ComboBoxItem >
<ComboBoxItem >khobi</ComboBoxItem >
<ComboBoxItem>سلام</ComboBoxItem>
<ComboBoxItem>خوبی</ComboBoxItem>
<ComboBoxItem>عرض ادب</ComboBoxItem>
<ComboBoxItem>سپاسگذارم</ComboBoxItem>
<ComboBoxItem>مرسی</ComboBoxItem>
</ComboBox>
Code behind
private void comboBox3_KeyUp(object sender, KeyEventArgs e)
{
var defaultView = (CollectionView)CollectionViewSource.GetDefaultView(comboBox3.Items);
var searchTerm = comboBox3.Text;
defaultView.Filter = (item) =>
{
var comboBoxItem = (ComboBoxItem)item;
if (string.IsNullOrEmpty(searchTerm) || ((string)comboBoxItem.Content).Contains(searchTerm))
{
return true;
}
else
{
return false;
}
};
defaultView.Refresh();
}

Referencing a databound ContextMenu inside a LongListSelector ItemTemplate - Windows Phone

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;

Silverlight Why doesn't this work

I am trying to create a slider(without binding).
Currently i did this:
Xaml:
<Slider Height="68" HorizontalAlignment="Left" Margin="52,45,0,0" x:Name="slider1" VerticalAlignment="Top" Width="256" Minimum="1" Maximum="40" Value="10" ValueChanged="slider1_ValueChanged" />
<TextBlock x:Name="textBlock1" Margin="52,120,0,0" Text="Slide it!" ></TextBlock>
And in my cs:
private void slider1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) {
textBloxk1.FontSize = slider1.Value;
}
But the silverlight page keeps loading and won't show the slider, anyone know what I'm doing wrong??
Probably at first ValueChanged event, slider1 and textblock1 are still null.
try this:
private void slider1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (textBlock1 != null && slider1 != null)
{
textBlock1.FontSize = slider1.Value;
}
}
look at your Xaml.. you setting value to 10 Value="10"... but at that time textBlock dosn't exist.. be carefull..
when parser parse Xaml it first create Slider then sets all values to slider (and fire all attached events), and only then it creates TextBlock...
so change you code to this, and everithing should be fine..
private void slider1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (textBlock1 != null && slider1 != null)
{
textBlock1.FontSize = slider1.Value;
}
}

Can I navigate into and out of a wpf combo box using arrow keys instead of tab?

I have wpf UserControl containing a combobox and a textbox in a row. Currently the only way to move between the components is to tab between them, but I would like to also be able to switch from the combobox to the textbox using the left and right arrow keys.
It is not as easy as just slapping an eventhandler on the keyup event.
void ComboKeyUp( object sender, KeyEventArgs e )
{
if( e.Key == Key.Right)
{
e.Handled = true;
textbox.Focus();
}
}
...because the combo will change value despite the event being reported as handled.
Is there a way to do this that doesn't simultaneously break the up/down selection of items in the combo box?
I have created the combobox with textboxes:
<ComboBox Width="100" Height="25" PreviewKeyDown="ComboboxPreviewKeyDown">
<ComboBox.Items>
<TextBox Text="Item 1"/>
<TextBox Text="Item 2"/>
<TextBox Text="Item 3"/>
</ComboBox.Items>
</ComboBox>
Then added handler:
private void ComboboxPreviewKeyDown(object sender, KeyEventArgs e)
{
Action<FocusNavigationDirection> moveFocus = focusDirection => {
e.Handled = true;
var request = new TraversalRequest(focusDirection);
var focusedElement = Keyboard.FocusedElement as UIElement;
if (focusedElement != null)
focusedElement.MoveFocus(request);
};
if (e.Key == Key.Down)
moveFocus(FocusNavigationDirection.Next);
else if (e.Key == Key.Up)
moveFocus(FocusNavigationDirection.Previous);
}
Now "Up" and "Down" buttons behavior are the same as "Tab" and "Shift+Tab"
It turns out there is a gotcha, you have subscribe to the KeyDown event instead.

WPF: Create a dialog / prompt

I need to create a Dialog / Prompt including TextBox for user input. My problem is, how to get the text after having confirmed the dialog? Usually I would make a class for this which would save the text in a property. However I want do design the Dialog using XAML. So I would somehow have to extent the XAML Code to save the content of the TextBox in a property - but I guess that's not possible with pure XAML. What would be the best way to realize what I'd like to do? How to build a dialog which can be defined from XAML but can still somehow return the input? Thanks for any hint!
The "responsible" answer would be for me to suggest building a ViewModel for the dialog and use two-way databinding on the TextBox so that the ViewModel had some "ResponseText" property or what not. This is easy enough to do but probably overkill.
The pragmatic answer would be to just give your text box an x:Name so that it becomes a member and expose the text as a property in your code behind class like so:
<!-- Incredibly simplified XAML -->
<Window x:Class="MyDialog">
<StackPanel>
<TextBlock Text="Enter some text" />
<TextBox x:Name="ResponseTextBox" />
<Button Content="OK" Click="OKButton_Click" />
</StackPanel>
</Window>
Then in your code behind...
partial class MyDialog : Window {
public MyDialog() {
InitializeComponent();
}
public string ResponseText {
get { return ResponseTextBox.Text; }
set { ResponseTextBox.Text = value; }
}
private void OKButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
DialogResult = true;
}
}
Then to use it...
var dialog = new MyDialog();
if (dialog.ShowDialog() == true) {
MessageBox.Show("You said: " + dialog.ResponseText);
}
Edit: Can be installed with nuget https://www.nuget.org/packages/PromptDialog/
I just add a static method to call it like a MessageBox:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
x:Class="utils.PromptDialog"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterScreen"
SizeToContent="WidthAndHeight"
MinWidth="300"
MinHeight="100"
WindowStyle="SingleBorderWindow"
ResizeMode="CanMinimize">
<StackPanel Margin="5">
<TextBlock Name="txtQuestion" Margin="5"/>
<TextBox Name="txtResponse" Margin="5"/>
<PasswordBox Name="txtPasswordResponse" />
<StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right">
<Button Content="_Ok" IsDefault="True" Margin="5" Name="btnOk" Click="btnOk_Click" />
<Button Content="_Cancel" IsCancel="True" Margin="5" Name="btnCancel" Click="btnCancel_Click" />
</StackPanel>
</StackPanel>
</Window>
And the code behind:
public partial class PromptDialog : Window
{
public enum InputType
{
Text,
Password
}
private InputType _inputType = InputType.Text;
public PromptDialog(string question, string title, string defaultValue = "", InputType inputType = InputType.Text)
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(PromptDialog_Loaded);
txtQuestion.Text = question;
Title = title;
txtResponse.Text = defaultValue;
_inputType = inputType;
if (_inputType == InputType.Password)
txtResponse.Visibility = Visibility.Collapsed;
else
txtPasswordResponse.Visibility = Visibility.Collapsed;
}
void PromptDialog_Loaded(object sender, RoutedEventArgs e)
{
if (_inputType == InputType.Password)
txtPasswordResponse.Focus();
else
txtResponse.Focus();
}
public static string Prompt(string question, string title, string defaultValue = "", InputType inputType = InputType.Text)
{
PromptDialog inst = new PromptDialog(question, title, defaultValue, inputType);
inst.ShowDialog();
if (inst.DialogResult == true)
return inst.ResponseText;
return null;
}
public string ResponseText
{
get
{
if (_inputType == InputType.Password)
return txtPasswordResponse.Password;
else
return txtResponse.Text;
}
}
private void btnOk_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
So you can call it like:
string repeatPassword = PromptDialog.Prompt("Repeat password", "Password confirm", inputType: PromptDialog.InputType.Password);
Great answer of Josh, all credit to him, I slightly modified it to this however:
MyDialog Xaml
<StackPanel Margin="5,5,5,5">
<TextBlock Name="TitleTextBox" Margin="0,0,0,10" />
<TextBox Name="InputTextBox" Padding="3,3,3,3" />
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Name="BtnOk" Content="OK" Grid.Column="0" Margin="0,0,5,0" Padding="8" Click="BtnOk_Click" />
<Button Name="BtnCancel" Content="Cancel" Grid.Column="1" Margin="5,0,0,0" Padding="8" Click="BtnCancel_Click" />
</Grid>
</StackPanel>
MyDialog Code Behind
public MyDialog()
{
InitializeComponent();
}
public MyDialog(string title,string input)
{
InitializeComponent();
TitleText = title;
InputText = input;
}
public string TitleText
{
get { return TitleTextBox.Text; }
set { TitleTextBox.Text = value; }
}
public string InputText
{
get { return InputTextBox.Text; }
set { InputTextBox.Text = value; }
}
public bool Canceled { get; set; }
private void BtnCancel_Click(object sender, System.Windows.RoutedEventArgs e)
{
Canceled = true;
Close();
}
private void BtnOk_Click(object sender, System.Windows.RoutedEventArgs e)
{
Canceled = false;
Close();
}
And call it somewhere else
var dialog = new MyDialog("test", "hello");
dialog.Show();
dialog.Closing += (sender,e) =>
{
var d = sender as MyDialog;
if(!d.Canceled)
MessageBox.Show(d.InputText);
}
You don't need ANY of these other fancy answers. Below is a simplistic example that doesn't have all the Margin, Height, Width properties set in the XAML, but should be enough to show how to get this done at a basic level.
XAML
Build a Window page like you would normally and add your fields to it, say a Label and TextBox control inside a StackPanel:
<StackPanel Orientation="Horizontal">
<Label Name="lblUser" Content="User Name:" />
<TextBox Name="txtUser" />
</StackPanel>
Then create a standard Button for Submission ("OK" or "Submit") and a "Cancel" button if you like:
<StackPanel Orientation="Horizontal">
<Button Name="btnSubmit" Click="btnSubmit_Click" Content="Submit" />
<Button Name="btnCancel" Click="btnCancel_Click" Content="Cancel" />
</StackPanel>
Code-Behind
You'll add the Click event handler functions in the code-behind, but when you go there, first, declare a public variable where you will store your textbox value:
public static string strUserName = String.Empty;
Then, for the event handler functions (right-click the Click function on the button XAML, select "Go To Definition", it will create it for you), you need a check to see if your box is empty. You store it in your variable if it is not, and close your window:
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
if (!String.IsNullOrEmpty(txtUser.Text))
{
strUserName = txtUser.Text;
this.Close();
}
else
MessageBox.Show("Must provide a user name in the textbox.");
}
Calling It From Another Page
You're thinking, if I close my window with that this.Close() up there, my value is gone, right? NO!! I found this out from another site: http://www.dreamincode.net/forums/topic/359208-wpf-how-to-make-simple-popup-window-for-input/
They had a similar example to this (I cleaned it up a bit) of how to open your Window from another and retrieve the values:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnOpenPopup_Click(object sender, RoutedEventArgs e)
{
MyPopupWindow popup = new MyPopupWindow(); // this is the class of your other page
//ShowDialog means you can't focus the parent window, only the popup
popup.ShowDialog(); //execution will block here in this method until the popup closes
string result = popup.strUserName;
UserNameTextBlock.Text = result; // should show what was input on the other page
}
}
Cancel Button
You're thinking, well what about that Cancel button, though? So we just add another public variable back in our pop-up window code-behind:
public static bool cancelled = false;
And let's include our btnCancel_Click event handler, and make one change to btnSubmit_Click:
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
cancelled = true;
strUserName = String.Empty;
this.Close();
}
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
if (!String.IsNullOrEmpty(txtUser.Text))
{
strUserName = txtUser.Text;
cancelled = false; // <-- I add this in here, just in case
this.Close();
}
else
MessageBox.Show("Must provide a user name in the textbox.");
}
And then we just read that variable in our MainWindow btnOpenPopup_Click event:
private void btnOpenPopup_Click(object sender, RoutedEventArgs e)
{
MyPopupWindow popup = new MyPopupWindow(); // this is the class of your other page
//ShowDialog means you can't focus the parent window, only the popup
popup.ShowDialog(); //execution will block here in this method until the popup closes
// **Here we find out if we cancelled or not**
if (popup.cancelled == true)
return;
else
{
string result = popup.strUserName;
UserNameTextBlock.Text = result; // should show what was input on the other page
}
}
Long response, but I wanted to show how easy this is using public static variables. No DialogResult, no returning values, nothing. Just open the window, store your values with the button events in the pop-up window, then retrieve them afterwards in the main window function.

Resources