How click a TextBlock in DataTemplate? - c#

This is my XAML code:
<ListBox ItemsSource="{Binding}" Name="listBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel Width="370">
<TextBlock Text="{Binding AuthorName}" x:Name="author" MouseEventLeftDown="click"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And the Click Handler
private void click(object sender, RoutedEventArgs e)
{
if(author.Text.Equals("Hi"))
{
// Do Something Special
}
}
The error is:
Error: The name 'author' does not exist in the current context
But I don't understand what is causing this error or why it is occurring.

Your TextBlock with the Name author doesn't exist in the scope of your click handler because it's in a DataTemplate. What's happening is that the author TextBlock is created once for every one of your data items (Presumably an Author class or a Book class of some kind), so you literally can have dozens of controls named author.
You are better off casting your sender in your click handler to a text box and then checking its text property. Something like this:
private void click(object sender, RoutedEventArgs args)
{
var textBox = sender as TextBox;
if(textBox == null)
return;
if(textBox.Text.Equals("hi"))
{
// Do Something Crazy!
}
}

It's probably better to use a UI element designed for touch, such as a HyperlinkButton or a Button. You can style these any way you would like to - especially if you use Expression Blend - but it is good design to include some visual feedback about the Touch.
Also - I'm not sure about your == code - you're comparing the sender (a UI element) against some string expression?

First off, your TextBlock is defined in a DataTemplate; try x:Name instead of Name on your TextBlock.
Secondly it might be quite tricky to click your TextBlock since you will have to press an exact pixel in your TextBlock. To make it easier to click your TextBlock you might want to put a Background on your TextBlock, so it will be a lot easier to click. You can even make the background transparent:
Background="Transparent"

use the gesture listener for create an event handler like "tap" or double" or whatever.

Use this...
private void click(object sender, RoutedEventArgs e)
{
var author = (TextBlock)sender;
if (author.Text.Equals("Hi"))
{
// Do Something Special
}
}

Related

Textbox event only fires the second time

question edited to provide a better explanation
I am using a treeview consisting of a stackpanel with a textblock and a textbox inside. What I would like to achieve is the total selection of the text when the textbox appears.
The textblock disappears by double-clicking or selecting an option from a context menu, giving visibility to the textbox to rename the item.
I'd like to have the selectall on the MouseLeftButtonDown on the textblock and also on click on a context menu option.
My treeview is contained in the MainWindow and, the stackpanel (with the text block and the textbox) is in a different file and I dynamically add it to the tree view depending on the user's action.
When I click on the StackPanel the first click highlights it, on double-click it opens a page and, on the MouseLeftButtonDown (and on click in a contextmenu option) I change the visibility of the textblock with the textbox and here I want the selectall() event to get fired.
I tried the following code and it only works halfway:
private void mniRename_Click(object sender, RoutedEventArgs e)
{
prevSelected.MyTextBlock.Visibility = Visibility.Collapsed;
prevSelected.MyTextBox.Visibility = Visibility.Visible;
prevSelected.MyTextBox.Focus();
if (prevSelected.MyTextBox.IsFocused)
{
prevSelected.MyTextBox.SelectAll();
}
prevSelected.MyTextBox.Text = prevSelected.MyTextBlock.Text;
}
The issue is that the SelectAll() event doesn't work on the first click while the Focus() works, then on the following clicks everything works fine.
The code is always executed in the same way.
Does anyone have any idea why this happens?
No really sure what you want to achieve. but what you describe can be achieved by the following code:
XAML
<StackPanel Orientation="Horizontal">
<TextBox x:Name="MyTextBox" LostFocus="MyTextBox_OnLostFocus" Width="100"/>
<TextBlock x:Name="MyTextBlock" Text="{Binding ElementName=MyTextBox, Path=Text}" MouseLeftButtonDown="MyTextBlock_OnMouseLeftButtonDown"/>
</StackPanel>
C#
private void MyTextBox_OnLostFocus(object sender, RoutedEventArgs e)
{
MyTextBox.Visibility = Visibility.Hidden;
MyTextBlock.Visibility = Visibility.Visible;
}
private void MyTextBlock_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MyTextBlock.Visibility = Visibility.Collapsed;
MyTextBox.Visibility = Visibility.Visible;
MyTextBox.Focus();
MyTextBox.SelectAll();
}

Passing a string parameter to a textblock

I am trying to pass a string parameter from another xaml page (upon click of a button) into a content dialog and display it inside a textblock in another colour.
Example of the textblock text:
Hey -parameter in red colour-, well -parameter in blue colour-, ... some text... -parameter in another colour-
My current method is to create several textblocks with different properties and then programmatically set the text to the corresponding textblock in the constructor.
There are too much redundant code and I believe there is a more elegant solution to this and I hope that someone could point me in the correct direction. Something tells me its binding but I am not sure how to proceed. (I'm new to XAML and trying to figure my way out by starting on something simple)
You can have an object set as the ContentDialog.DataContext and then use binding to achieve what you want.
In your Button.Click handler, set the data context:
private void Button_Click(object sender, RoutedEventArgs args)
{
ContentDialog dialog = new ContentDialog
{
DataContext = new
{
RedText = "Red Colour",
BlueText = "Blue Colour"
}
};
dialog.ShowAsync();
}
Then in the XAML of the ContentDialog, you can have something as:
<ContentDialog>
<TextBlock>Hey <TextBlock Background="Red" Text="{Binding RedText}"/>, well <TextBlock Background="Blue" Text="{Binding BlueText}"/></TextBlock>
</ContentDialog>

Working with elements inside a control that is bound to a collection

I have an ObservableCollection<string> that is bound to an ItemsControl whose template is just a Button. The content of this button are 2 TextBlock. I'm trying to use the PreviewMouseRightButtonUp event of the button to toggle the visibility of one of the textblocks, but without being able to use xaml names for elements in the template I'm hitting a wall. Is there a way of getting to the button's content elements via sender in that preview event, or some other way of doing this? This is related to a previous question I had that didn't quite get a usable answer (probably due to my explanation, hence this simplified example). It seems to me that what should happen is I should make a control based off button that adds a property for this toggle, but that is basically what I thought I had in the previous question that wasn't working. I feel like a property and trigger is what most would say is the right way to go?
xaml:
<ItemsControl x:Name="iC" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button PreviewMouseRightButtonUp="Button_PreviewMouseRightButtonUp">
<DockPanel>
<TextBlock Text="normal" DockPanel.Dock="Top"/>
<TextBlock Text="{Binding}" DockPanel.Dock="Top" Visibility="Collapsed"/>
</DockPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
code behind:
ObservableCollection<string> x = new ObservableCollection<string>();
public MainWindow()
{
x.Add("1");
x.Add("2");
InitializeComponent();
iC.ItemsSource = x;
}
If you name the hidden text block "secondTextBlock", then this should work:
private void Button_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
DockPanel dockPanel = (DockPanel)((Button)sender).Content;
TextBlock text = (TextBlock)LogicalTreeHelper.FindLogicalNode(dockPanel, "secondTextBlock");
if (text != null)
{
text.Visibility = Visibility.Visible;
}
}
Regarding your comment below: yes, multiple instances of "secondTextBlock" will be created. See the Snoop screenshot below. But these multiple instances are OK; they do not have any negative impact.

Getting value back from ListBox to EventHandler in WP7

In my WP7 application I have ListBox control that binds with List<ItemTemplate> collection. On each ListBoxItem I have Click event which navigates to DisplayItem.xaml. Each ItemTemplate object has Id property which has to be passed to DispalyItem.xaml. I know that I can pass this value from Click EventHandler to DisplayItem.xaml using QueryString but how do I pass it from ListBox item to EventHandler ?
<ListBox x:Name="listBoxItems">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Fill="red" Width="30" Height="30"></Ellipse>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Status}" FontSize="35" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<HyperlinkButton Content="{Binding ItemContent}" Name="itemButton" Click="itemButton_Click"/>
</StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="edit"/>
<toolkit:MenuItem Header="delete"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Id property is not mentioned in the above code because I just simply didnt know where to place it. Generally I want to know how can I get Id property back to the click EventHandler ? I'm sorry if this question is basic for you but I'm new to that and i wasn't sure how to google that.
If you're really new to Windows Phone 7, you may want to stop using the "Click" event and instead use the ListBox.SelectionChanged event. If you are bound to List<MyObject>, you could do the following:
In your XAML:
<ListBox SelectionChanged="NavigateToMyDetail" ... >
Then in the code behind, you would have something like this:
private void NavigateToMyDetail(object sender, SelectionChangedEventArgs e)
{
// Make sure that the ListBox change wasn't due to a deselection
if(e.AddedItems != null && e.AddedItems.Count == 1)
{
MyObject selectedItem = (MyObject)e.AddedItems[0];
// Now you have access to all your MyObject properties
// and you can pass that to your new page as a parameter
NavigationService.Navigate(new Uri("DisplayItem.xaml?ItemID=" + selectedItem.id.ToString(), UriKind.Relative));
}
}
And you can get that ID with the following code (probably in your "OnNavigatedTo" method).
string myItemID = null;
if(this.NavigationContext.QueryString.ContainsKey("ItemID"))
myItemID = NavigationContext.QueryString["ItemID"];
Hope that helps. The other way to try to get it is to give your ListBox a x:Name and then references it in your Click handler like:
private void MyClickHandler(object sender, System.Windows.RoutedEventArgs e)
{
MyObject selectedObject = (MyObject)MyListBoxName.SelectedItem;
}
There is a much simpler solution if you use data binding with an MVVM viewmodel behind it.
Simply bind you view to a property in the view model for the listbox "Source" and then also do the same for the ListBox "SelectedItem" or "SelectedIndex" properties, then you will have all you need accessible where ever you needed.
Only think to be aware of (as I'm uncertain if it ever got fixed) is to fixed the selected index property when an item has been selected, if you do not reset it to -1 then if the user returns to the list they cannot select the same item. (do this in the codebehind for the click event)
Also if you use MVVM and databinding you can enact an action from the change of the Selected item rather than using Code behind to drive the direction, always an option to keep things simple (but not mandatory)
I have also came to my own solution. I'm not sure If its correct bit its certainly solving my problem for now.
I found this CommandParameter property of object HyperlinkButton. I bound my MyObject.Id property value to it.
<HyperlinkButton Content="{Binding ItemContent}" Click="itemButton_Click" CommandParameter="{Binding Id}" />
Then in my EventHandler i said:
private void itemButton_Click(object sender, RoutedEventArgs e)
{
HyperlinkButton butt = sender as HyperlinkButton;
NavigationService.Navigate(new Uri("/ViewItem.xaml?itemId=" + butt.CommandParameter.ToString(), UriKind.Relative));
}
It works as I need it to work but I'm not sure If i should use it in my applications in the future.

How to keep WPF TextBox selection when not focused?

I want to show a selection in a WPF TextBox even when it's not in focus. How can I do this?
I have used this solution for a RichTextBox, but I assume it will also work for a standard text box. Basically, you need to handle the LostFocus event and mark it as handled.
protected void MyTextBox_LostFocus(object sender, RoutedEventArgs e)
{
// When the RichTextBox loses focus the user can no longer see the selection.
// This is a hack to make the RichTextBox think it did not lose focus.
e.Handled = true;
}
The TextBox will not realize it lost the focus and will still show the highlighted selection.
I'm not using data binding in this case, so it may be possible that this will mess up the two way binding. You may have to force binding in your LostFocus event handler. Something like this:
Binding binding = BindingOperations.GetBinding(this, TextProperty);
if (binding.UpdateSourceTrigger == UpdateSourceTrigger.Default ||
binding.UpdateSourceTrigger == UpdateSourceTrigger.LostFocus)
{
BindingOperations.GetBindingExpression(this, TextProperty).UpdateSource();
}
Another option is to define a separate focus scope in XAML to maintain the selection in the first TextBox.
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="Text that does not loose selection."/>
<StackPanel Grid.Row="1" FocusManager.IsFocusScope="True">
<TextBox Text="Some more text here." />
<Button Content="Run" />
<Button Content="Review" />
</StackPanel>
</Grid>
TextBoxBase.IsInactiveSelectionHighlightEnabled Property has available since .NET Framework 4.5
public bool IsInactiveSelectionHighlightEnabled { get; set; }
public class CustomRichTextBox : RichTextBox
{
protected override void OnLostFocus(RoutedEventArgs e)
{
}
}
I found that the suggestions listed (add a LostFocus handler, defining a FocusScope) to not work, but I did come across the code listed here: http://naracea.com/2011/06/26/selection-highlight-and-focus-on-wpf-textbox/, which creates a custom Adorner that highlights the text when not focused.

Categories