I developed a take-note app, used the ItemsNote to save the list of notes, and the ItemModifyNote to save the temporary item when modify.
public ObservableCollection<NoteViewModel> ItemsNote
{
get
{
return _itemsNote;
}
set
{
_itemsNote = value;
NotifyPropertyChanged("ItemsNote");
}
}
public NoteViewModel ItemModifyNote { get; set; }
at Mainpage.xaml ( where I display the ItemsNote binding within a LongListSelector), I insert a "edit" button next to each note, so when I click it, I set the ItemModifyNote's data to selected item in ItemsNote, then navigate to the "modifyNotePage.xaml"
private void btEditNote_Click(object sender, RoutedEventArgs e)
{
var button = (sender as Button).DataContext as NoteViewModel;
if (button != null)
{
int intIndex = App.ViewModel.ItemsNote.IndexOf(button);
string modifyUri = "/Pages/NoteModifyPage.xaml?Id=" + intIndex.ToString();
App.ViewModel.ItemModifyNote = App.ViewModel.ItemsNote.ElementAt(intIndex);
NavigationService.Navigate(new Uri(modifyUri, UriKind.RelativeOrAbsolute));
}
}
at the ModifyNotePage.xaml, I modify the data of ItemModifyNote (which include a title and a content, both are string) by 2 textbox
<TextBox Grid.Column="1"
Text="{Binding ItemModifyNote.NoteTitle, Mode=TwoWay}" x:Name="tbxModifyNoteTitle"
FontFamily="Clear Sans Light" BorderThickness="0.0"
KeyDown="tbxModifyNoteTitle_KeyDown"/>
</Grid>
<TextBox Grid.Row="1" Margin="0,0,0,20"
x:Name="tbxModifyNoteContent" Text="{Binding ItemModifyNote.NoteContent, Mode=TwoWay}"
AcceptsReturn="True" TextWrapping="Wrap" BorderThickness="0.0" FontFamily="Clear Sans Light"
GotFocus="tbxModifyNoteContent_GotFocus" LostFocus="tbxModifyNoteContent_LostFocus"/>
finally I use 2 buttons: Cancel and Save.
In Save button I set the data of item in ItemsNote by the data of ItemModifyNote
private void btCancel_Click(object sender, EventArgs e)
{
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.RelativeOrAbsolute));
}
private void btSave_Click(object sender, EventArgs e)
{
App.ViewModel.ItemsNote[key] = App.ViewModel.ItemModifyNote;
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.RelativeOrAbsolute));
}
the problem is: even when I click the cancel button, the note still save the modify text ???
That's because ItemModifyNote references NoteViewModel instance from ItemsNote. Since both TextBox in edit page and LongListSelector in the main page operating on the same veiwmodel instance, when user modify ItemModifyNote property, LongListSelector will display updated value without any more code needed.
To avoid this behavior, in button edit click event handler method, try to create a new instance of NoteViewModel and copy it's properties value from the one in ItemsNote, instead of directly referencing the existing instance.
Related
I have a Button with a TextBlock embedded inside. When the Button is clicked, I want to be able to fetch the TextBlock inside it and modify it's members.
Here is how my button is setup:
<Button Click="Select_Click" Style="{StaticResource ButtonStyle}" HorizontalAlignment="Left" Padding="0,20,20,20">
<TextBlock Text="My text" FontSize="20" Style="{StaticResource TextBlockStyle}"/>
</Button>
In my code behind I want to be able to access the embedded TextBlock:
public void Select_Click(object sender, RoutedEventArgs e)
{
// Get the `TextBlock` from `sender` here
}
I've taken a look at the visual tree of the Button but I'm not seeing the TextBlock. I called GetVisualChildren() on the Button but I only see a Grid and no way to get to the Textblock.
The content of the Button is stored in its Content property and in your case, the TextBlock is the content of the Button.
public void Select_Click(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
TextBlock textBlock = (TextBlock)button.Content;
}
Just do some casting and it's pretty simple
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Establish_handlers();
}
void Establish_handlers()
{
Mybutton.Click += Mybutton_Click;
}
private void Mybutton_Click(object sender, RoutedEventArgs e)
{
Button clicked_button = (Button)sender;
TextBlock desired_text = (TextBlock)clicked_button.Content;
Textbox_Show_Button_Content.Text = desired_text.Text;
}
}
<StackPanel>
<Button x:Name="Mybutton">
<TextBlock>Hello</TextBlock>
</Button>
<TextBox x:Name="Textbox_Show_Button_Content"></TextBox>
</StackPanel>
I have a series of textboxes in a usercontrol that do not have accompanying Labels.
I need to provide a means for a user to press Alt+(Some Key) to set focus to each of these textboxes.
If I want to use the built in WPF "AccessText" way of doing this, I would need to put a Label up next to each textbox, specify the content with an '_' character preceding the shortcut key, and specify the "Target" property of each Label to their respective Textbox.
Unfortunately in this case, there are no Labels for each textbox, and there will not be.
Is it possible to specify the AccessText shortcut key for a textbox, without a Label?
you can Handle key combinations press and after that focus on the TextBox :
*.xaml:
<UserControl Loaded="UserControl_Loaded">
<Grid>
<StackPanel>
<TextBox Height="30" x:Name="txt1"/>
<TextBox Height="30" x:Name="txt2"/>
</StackPanel>
</Grid>
</UserControl>
*.cs
public UserControl()
{
InitializeComponent();
}
private void KeyDownEvent(object sender, KeyEventArgs e)
{
bool x = Keyboard.IsKeyDown(Key.System);
if (Keyboard.IsKeyDown(Key.System) && Keyboard.IsKeyDown(Key.B))// Alt+B
{
txt2.Focusable = true;
txt2.Focus();
}
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
var window = Window.GetWindow(this);
window.KeyDown += KeyDownEvent;
txt1.Focusable = true;
txt1.Focus();
}
}
I have a ListPicker in an application page, but the SelectionChanged event gets called multiple times as the page loads. To avoid this, I have been following a previous question I asked here ListPicker SelectionChanged Event Called Multiple Times During Navigation in which the suggestion was instead of making ThemeListPicker_SelectionChanged make a parent stackpanel inside the datatemplate..', create a tap event in the StackPanel called stk_Tap, and 'use this tap stk_Tap to do your action as, this event would also get called every time the selection changed gets called but, it wont exhibit the buggy behavior like that of selection changed event'
Now I have adjusted my solution accordingly, but I do not know how to determine which item of the ListPicker is being selected or is currently selected. Also I removed the ListPicker SelectionChanged event in the ListPicker because I thought the StackPanel could get the item, but I am not sure if this is correct or how to do this?
XAML
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Name="PickerItemTemplate">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<toolkit:ListPicker x:Name="ThemeListPicker" Header="Theme"
ItemTemplate="{StaticResource PickerItemTemplate}"
SelectionChanged="ThemeListPicker_SelectionChanged"/>
XAML.CS
private void ThemeListPicker_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
if(ThemeListPicker.SelectedIndex != -1)
{
var theme = (sender as ListPicker).SelectedItem;
if (index == 0)
{
Settings.LightTheme.Value = true;
MessageBox.Show("light");
}
else
{
Settings.LightTheme.Value = false;
MessageBox.Show("dark");
}
}
}
*EDIT: How I updated my solution
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Name="PickerItemTemplate">
<StackPanel tap="stk_Tap">
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<toolkit:ListPicker x:Name="ThemeListPicker" Header="Theme"
ItemTemplate="{StaticResource PickerItemTemplate}"
/>
So, even when I left the ListPicker SelectionChanged event in the code behind after making the modifications, I did not see the event being called twice upon the page loading/navigating to, but I am not sure how to get the currently selected item now?
EDIT2**
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
themeList = new List<TestApp.Common.Theme>();
themeList.Add(new TestApp.Common.Theme() { Name = "Darker", name = "dark" });
themeList.Add(new TestApp.Common.Theme() { Name = "Lighter", name = "light" });
ThemeListPicker.ItemsSource = themeList;
}
private void stk_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
if (ThemeListPicker.SelectedIndex != -1)
{
//Need to get the current ThemeListPicker's 'name'
var selectedItem1 = (sender as StackPanel).DataContext as ListPicker;
//use selectedItem1
}
}
No need to extra tap event for such kind of work.
private void ThemeListPicker_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
if(ThemeListPicker.SelectedIndex==-1)
return;
var theme = (sender as ListPicker).SelectedItem;
if (index == 0)
{
Settings.LightTheme.Value = true;
MessageBox.Show("light");
}
else
{
Settings.LightTheme.Value = false;
MessageBox.Show("dark");
}
ThemeListPicker.SelectedIndex=-1
}
ListPicker SelectionChanged Event Called Multiple Times During Navigation
for above problem if i guess right you set listpicker's itemssource on OnNavigatedTo event. so modify you r onNavigatedTo method with
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.NavigationMode != NavigationMode.Back)
{
// Your code goes here
}
}
//Stack panel tap event
private void stack_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
var selectedIrem = (Cast as your type)(sender as StackPanel).DataContext;
}
I've a list of data,
Each row will show a data and will have a button, when i click the data shown i want give some data to the previous page and when i click the button in the same row i want to send that same data to next page.
My Xaml code,
<ListBox x:Name="List" HorizontalAlignment="Left" Height="612" Margin="6,7,0,0" VerticalAlignment="Top" Width="443" SelectionChanged="List_SelectionChanged_1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="420" Height="50">
<TextBlock x:Name="tbName" Width="400" Height="44" FontSize="22" FontWeight="Bold" Text="{Binding Name}" />
<Button x:Name="DetailButton" Height="44" Width="20" Content=">" FontWeight="Bold" Click="DetailButton_Click_1"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and the code for List_SelectionChanged_1 event handler is,
private void List_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
Display selectedItemData = (sender as ListBox).SelectedValue as Display;
NavigationService.Navigate("/Page1.xaml",selectedItemData);
}
and my DetailButton_Click_1 event handler is,
private void DetailButton_Click_1(object sender, RoutedEventArgs e)
{
Display selectedItemData = (sender as ListBox).SelectedValue as Display;
NavigationService.Navigate("/page3.xaml", selectedItemData);
}
Things work fine for *List_SelectionChanged_1*, but i get an exception while executing
Display selectedItemData = (sender as ListBox).SelectedValue as Display;
of the DetailButton_Click_1 , i get an exception a null exception,
An exception of type 'System.NullReferenceException' occurred in ExpenseApp.DLL but was not handled in user code
What should i do make it work?
The underlying problem is that the sender of the button click event is the button, not the ListBox.
Also note that clicking the button on your data template will not necessarily select that item in the list. Try to grab the clicked item's data context and use that instead of .SelectedItem
private void DetailButton_Click_1(object sender, RoutedEventArgs e)
{
var clickedUIElement = sender as Button;
if (null == clickedUIElement) { Return; }
Display selectedItemData = clickedUIElement.DataContext as Display;
if(null != selectedItemData)
{
NavigationService.Navigate("/page3.xaml", selectedItemData);
}
}
Your code, as it stands, will have a null reference since you can't cast a Button as a ListBox.
try verify if the selectvalue is null before execute the code:
private void DetailButton_Click_1(object sender, RoutedEventArgs e)
{
If ((sender as ListBox).SelectedValue != null){
Display selectedItemData = (sender as ListBox).SelectedValue as Display;
NavigationService.Navigate("/page3.xaml", selectedItemData);
}
}
I have a list box displaying the names of help topics which can be added to and the names of the topics changed. Originally it was just displaying strings, but to get the inline editing working I changed it to use a custom type consisting of a string and an InEdit property so the UI can determine whether to display the TextBlock or TextBox:
XAML:
<ListBox ItemsSource="{Binding HelpTopics, Mode=TwoWay}"
SelectedValuePath="Description"
SelectedValue="{Binding SelectedPageId, Mode=TwoWay}"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Description, Mode=TwoWay}"
VerticalAlignment="Center"
MouseLeftButtonUp="TopicTextBlock_MouseLeftButtonUp"
Visibility="{Binding InEdit, Converter={StaticResource boolToVisibilityConverter}, ConverterParameter=contra}"/>
<TextBox Text="{Binding Description, Mode=TwoWay}"
Visibility="{Binding InEdit, Converter={StaticResource boolToVisibilityConverter}, ConverterParameter=pro}"
LostFocus="EditTopicTextBox_LostFocus"
HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Margin="5" Content="Add Topic" Command="{Binding AddTopicCommand}"/>
HelpTopics is an ObservableCollection<EditableHelpTopic>.
SelectedPageId is a string.
boolToVisibilityConverter is a converter that does what it says.
What works:
Adding a topic creates a new item and adds it to the list and put the item in to edit mode.
Double clicking on an existing item puts that item into edit mode sets the focus to the TextBox and selects all the text so it can be overwritten.
When the TextBox loses focus the edit is saved and the display returns to the TextBlock.
What doesn't work:
When a new topic is added the TextBox should have focus and the text selected so the user can enter a new name.
So my question is is there a point in the code or an event where I know that the TextBox has been created and is visible so I can set focus and select its contents. I've tried hooking into the SelectionChanged event but when that fires the TextBox hasn't yet been displayed. I also added an event to the OnAddTopicExecute method in the view model which I handled in the view, but again that fired before the TextBox was visible.
Below is the code that supports the above XAML. I've tried to cut it down, but there still seems to be a lot of it, so you can skip this if you're not interested ;)
Code behind:
private DateTime lastClickTime = DateTime.MinValue;
private Point lastClickPosition;
private void TopicTextBlock_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
UIElement element = sender as UIElement;
if ((DateTime.Now - this.lastClickTime).TotalMilliseconds > 300)
{
this.lastClickPosition = e.GetPosition(element);
this.lastClickTime = DateTime.Now;
}
else
{
Point position = e.GetPosition(element);
if (Math.Abs(this.lastClickPosition.X - position.X) < 4 && Math.Abs(this.lastClickPosition.Y - position.Y) < 4)
{
var textBlock = sender as TextBlock;
var editableHelpTopic = textBlock.DataContext as EditableHelpTopic;
editableHelpTopic.InEdit = true;
var parent = textBlock.Parent as Grid;
TextBox textBox = parent.Children.First(c => c.GetType() == typeof(TextBox)) as TextBox;
textBox.Focus();
textBox.SelectAll();
}
}
}
private void EditTopicTextBox_LostFocus(object sender, RoutedEventArgs e)
{
var textBox = sender as TextBox;
var editableHelpTopic = textBox.DataContext as EditableHelpTopic;
editableHelpTopic.InEdit = false;
if (!textBox.Text.Equals(editableHelpTopic.Description))
{
this.editViewModel.RenameTopic(textBox.Text);
}
}
View Model:
public EditViewModel()
{
...
this.AddTopicCommand = new DelegateCommand(this.OnAddTopicExecute, this.OnAddTopicCanExecute);
...
}
where DelegateCommand is an implemetation of ICommand.
private void OnAddTopicExecute(object parameter)
{
var newTopic = new EditableHelpTopic
{
Description = "NewTopic",
InEdit = true
};
this.HelpTopics.Add(newTopic);
this.SelectedPageId = newTopic.Description;
}
Definitions:
public class EditableHelpTopic : INotifyPropertyChanged
{
public bool InEdit { ... }
public string Description { ... }
}
It turned out to be simpler than I thought.
I just needed to add a Loaded event handler to the TextBox:
private void EditTopicTextBox_Loaded(object sender, RoutedEventArgs e)
{
var textBox = sender as TextBox;
var editableHelpTopic = textBox.DataContext as EditableHelpTopic;
if (editableHelpTopic.InEdit)
{
textBox.Focus();
textBox.SelectAll();
}
}