I'm using a ListView with more than one DataTemplate. I'm trying to detect when the user clicks on an Item to show a Flyout.
GridItem_Holding is never called.
XAML:
<DataTemplate x:Key="myKey1">
<Grid Holding="GridItem_Holding">
...
</Grid>
</DataTemplate>
<DataTemplate x:Key="myKey2">
<Grid Holding="GridItem_Holding">
...
</Grid>
</DataTemplate>
Code behind:
private void GridItem_Holding(object sender, HoldingRoutedEventArgs e)
{
}
Try like this
<Grid x:Name="GridListItem" Holding="Grid_Holding">
Have you remembered IsHoldingEnabled on Grid or children? Holding event is not triggered unless it is set true.
Related
So I'm working on a calculator, basically a copy of the Windows Version, as a training excercise. I have implemented a History of past calculations, and I was asked to transform this history from TextBox to Listview.
What I want to do is copy one of the past calculations back into the Calculator TextBox when I click on it, just like in the Windows Calculator.
My ListViewCode:
<ListView Grid.Column="0" Grid.Row="1" Foreground="#616161" Name="history" Background="Transparent"
HorizontalAlignment="Stretch" BorderThickness="0" Margin="10,10,10,0">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<EventSetter Event="MouseLeftButtonDown" Handler="RetrievePastCalculation" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
And this is the RetrievePastCalculation method, but it doesn't work, nothing happens when I click on a ListViewItem. I'm new to WPF by the way.
private void RetrievePastCalculation(object sender, MouseButtonEventArgs e)
{
innerTextBox.Text = history.SelectedItems.ToString();
}
This is where I add items to the ListView I think, it's the Equal button method:
private void ButtonEquals_Click(object sender, RoutedEventArgs e)
{
Calculator calculate = new Calculator();
textBox.Text = calculate.Calculate(innerTextBox.Text);
history.Items.Add(innerTextBox.Text + "=" + textBox.Text);
innerTextBox.Clear();
}
history.SelectedItems is a collection, so calling ToString on it won't give you anything other than the name of the type. If you try it in the debugger (which you should), you'll see that it returns System.Windows.Controls.SelectedItemCollection. Now, at this point you can either fix your issue one of two ways: you can continue to use your current event-based approach, or you can use binding.
Events
With events, you can hook a handler to the Selected event for each ListItem that you add to the list:
private void ButtonEquals_Click(object sender, RoutedEventArgs e)
{
Calculator calculate = new Calculator();
textBox.Text = calculate.Calculate(innerTextBox.Text);
var item = new ListViewItem();
item.Content = innerTextBox.Text + "=" + textBox.Text;
item.Selected += HistoryItem_Selected //hooks the handler to the 'Selected' event
history.Items.Add(item);
innerTextBox.Clear();
}
then define the handler itself:
private void HistoryItem_Selected(object sender, RoutedEventArgs e)
{
// here 'sender' will be the ListItem which you clicked on
// but since it's an object we need to cast it first
ListViewItem listItem = (ListViewItem)sender;
// now all that's left is getting the text and assigning it to the textbox
innerTextBox.Text = listItem.Content.ToString();
}
Binding
Binding is much simpler as far as the amount of code is concerned, but has a steeper learning curve. Here, instead of setting the TextBox.Text property directly, we will specify a binding expression. This means that the value will always be the same as that of the bound expression.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<ListView Grid.Column="0" Grid.Row="0" Name="history" />
<TextBox Text="{Binding ElementName=history, Path=SelectedItem.Content}" />
<Button Name="ButtonEquals" Content="equals" Click="ButtonEquals_Click"/>
</StackPanel>
</Grid>
</Window>
I've run this in a new WPF project and it works as expected: the text box displays whatever text is in the clicked item from the list.
One thing to note is that both solutions assume that you are assigning strings to the ListViewItem Content. As you may know, you can assign other controls or any object to the Content property of a UI Control (ListViewItem inherits from Control). That's why the ListViewItem.Add method takes an argument of type object and is not restricted to one of type string. If you assigned anything other than a string in your button click event handler, both of the two cases above would likely break.
You could bind the value of the TextBox to the SelectedItem of the ListView. Here's an example:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<ListView Grid.Column="0" Grid.Row="0" Foreground="#616161" Name="history" Background="Transparent"
HorizontalAlignment="Stretch" BorderThickness="0" Margin="10,10,10,0">
<ListViewItem>Calc1</ListViewItem>
<ListViewItem>Calc2</ListViewItem>
</ListView>
<TextBox Text="{Binding ElementName=history, Path=SelectedItem.Content}" />
</StackPanel>
</Page>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="100"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListView Grid.Column="0" Grid.Row="0" Foreground="#616161" Name="history" BorderThickness="1,1" Height="50" Width="200" SelectionChanged="history_SelectionChanged">
<ListViewItem>
<TextBlock> A ListView</TextBlock>
</ListViewItem>
<ListViewItem>
with several
</ListViewItem>
<ListViewItem>
items
</ListViewItem>
</ListView>
<TextBox Grid.Row="1" Text="{Binding ElementName=history,Path=SelectedValue.Content}"
BorderThickness="1,1" Height="50" Width="200" />
</Grid>
It's better if you do it using XAML code. try to select item 0 and 1 to see the difference and understand how listboxworks.
now replace the text of textbox binding with following:
Text="{Binding ElementName=history,Path=SelectedValue.Content.Text}"
and seee the output for item 0. Hopefully you'll achieve desired output with a lot less effort.
Now that you have explained the whole problem i think you need to implement a converter in the text binding of TextBox. like below text
Text="{Binding ElementName=history,Path=SelectedValue.Content.Text,Converter={StaticResource mytextconverter}}"
and write down a logic to extract a part of text on the basis of '=' char. It's very easy to write a converter class. to write a converter follow the below link:
WPF Converter example
I have a ToolBar with a ItemsTemplate which works fine until the OverflowPanel is Available.
The OverflowPanel does not close if i select one of the context actions.
The Problem only occurs if the Items are added via the ItemsSource binding:
<ToolBar ItemsSource="{Binding ContextActionViewModels}"
Background="Transparent"
ToolBarTray.IsLocked="True"
FocusManager.IsFocusScope="False">
<ToolBar.ItemTemplateSelector>
<views:ContextActionTemplateSelector>
<views:ContextActionTemplateSelector.SimpleContextActionDataTemplate>
<DataTemplate DataType="{x:Type viewModels:SimpleContextActionViewModel}">
<Button Name="Button"
Command="{Binding ActionCommand}"
Style="{StaticResource ToolBarButtonStyle}"
ToolTip="{userInterface:Translation Binding={Binding ToolTip}}">
<ContentControl Template="{Binding Icon,Converter={StaticResource NameToResourceConverter}}"
Margin="5"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Button>
</DataTemplate>
</views:ContextActionTemplateSelector.SimpleContextActionDataTemplate>
<!-- Multiple DataTemplates follow!-->
Why is the DataTemplate / ItemTemplteSelector not working properly. While hardcoded Buttons in XAML work properly?
I uploaded a full sample that illustrates what is not working here:
Just Resize the window and try invoking one off the buttons in the OverflowPanel. While the 'ICommand' is executed properly the Popup stays open.
In the .NET framework source you can find the method that handles the closing behavior of OverflowPanel for ToolBar class:
private static void _OnClick(object e, RoutedEventArgs args)
{
ToolBar toolBar = (ToolBar)e;
ButtonBase bb = args.OriginalSource as ButtonBase;
if (toolBar.IsOverflowOpen && bb != null && bb.Parent == toolBar)
toolBar.Close();
}
When you define a DataTemplate and use ItemsSource property, the Parent property of the created button becomes null and the if check fails. This is the expected behavior of DataTemplate as described here:
For templates, the Parent of the template eventually will be null. To get past this point and extend into the logical tree where the template is actually applied, use TemplatedParent.
As a solution you can set the IsOverflowOpen property to false when you click any of the buttons:
<ToolBar Name="SomeToolBar" ItemsSource="{Binding Items}">
<ToolBar.ItemTemplate>
<DataTemplate DataType="local:ItemViewModel">
<Button Command="{Binding Command}" Content="{Binding Name}" Click="ButtonBase_OnClick"/>
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
SomeToolBar.IsOverflowOpen = false;
}
I'm developing a windows phone 8.1 app in XAML and C#. I have a ListView getting its Items from a bound list and displaying them through a DataTemplate. Now, in this DataTemplate there are multiple child elements, and when the user taps on an item in the list, I want to be able to determine what child element he actually touched. Depending on that, the app should either expand a view with more details inside the Item, or navigate to another page.
The ItemClick event handler of the ListView is ListView_ItemClick(object sender, ItemClickEventArgs e), and I thought e.OriginalSource would maybe give me the answer, but this just gave me the clicked ListItem.
I have yet to try if encapsulating the children with buttons and intercepting their click events would work, but I'm happy to try any alternative there might be for this.
I just found the solution myself. I set the ListView to SelectionMode="None" and IsItemClickEnabled="False", and then I added Tapped handlers for the individual child elements. Works just as I wanted.
I've got a TextBlock and an Image in one ListViewItem and have just used the Image_PointerPressed event. Doing that also fires the ItemClick event for the ListView so I disable it first, do the stuff I want, then re-enable the ItemClick event so that still fires when the TextBlock is pressed.
Code behind:
private async void imgDone_PointerPressed(object sender, PointerRoutedEventArgs e)
{
// disable click event so it won't fire as well
lvwCouncils.IsItemClickEnabled = false;
// do stuff
MessageDialog m = new MessageDialog("User Details");
await m.ShowAsync();
// Re-enable the click event
lvwCouncils.IsItemClickEnabled = true;
}
Xaml:
<ListView x:Name="lvwCouncils" ItemClick="lvwCouncils_ItemClicked" IsItemClickEnabled="true" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock
Grid.Column="1"
Text="{Binding council_name}"
FontSize="24"
Margin="10,10,30,10"
/>
<Border Height="20" Width="20" Margin="10,10,0,10" >
<Image x:Name="imgDone"
Source="Assets/user_earth.png" Stretch="UniformToFill" PointerPressed="imgDone_PointerPressed"/>
</Border>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Use the SelectionChanged event.
Cast the sender object to ListView type and then retrieve the item from the SelectedItem property.
Similar question here but for a different control :
Get the index of the selected item in longlistselector
Problem
When I click on a ListView item, it calls the "Tapped" event to navigate to another page. I have an Up Vote event within the ItemTemplate and when they call that specific event, I DO NOT want to call the ListView's tapped event. Any idea how I might do that?
ListView XAML:
Parent event, "listboxFeedbackItem_Tapped", occurs anytime any part of the listview is clicked
<Grid x:Name="gridMainData" Grid.Row="2">
<ProgressBar x:Name="prgBar" IsIndeterminate="True" VerticalAlignment="Top" Visibility="{Binding Path=FeedbackVM.IsLoading, Mode=TwoWay}"/>
<ListView ItemTemplate="{StaticResource FeedbackTemplate}" ItemsSource="{Binding FeedbackVM.FeedbackCollection}" Tapped="listboxFeedbackItem_Tapped"/>
</Grid>
ItemTemplate Xaml:
Event "UpVoteItem_Tap" should not trigger "listboxFeedbackItem_Tapped"
<DataTemplate x:Key="FeedbackTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0,0,30,0" Text="{Binding UpVotes}" Tapped="UpVoteItem_Tap"/>
</StackPanel>
</DataTemplate>
Perhaps there's a method in C# to prevent subsequent events from occurring?
Thanks, I'm still trying to wrap my head around XAML.
When you receive the UpVote tapped event, you can tell it not to pass the event to the parent listview by setting e.Handled=true:
void UpVoteItem_Tap(object sender, TappedRoutedEventArgs e)
{
// Processing here
...
// don't send event to parent
e.Handled = true;
}
Okay, so I'm fairly new to WPF and data binding, but I've searched and searched and can't seem to find an answer to this.
I have a database (called Inventory), and a dataset (called DevicesDataSet). What I'm trying to do is bind the dataset to a listbox, so that a specific device from the Devices table of the DevicesDataSet can be selected, and have its properties displayed in a TextBox for editing.
The following is the XAML I have so far:
<Window x:Class="Inventory.SignOutDevice"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SignOutDevice" Height="339" Width="392" xmlns:my="clr-namespace:Inventory" Loaded="Window_Loaded">
<Window.Resources>
<my:DevicesDataSet x:Key="devicesDataSet" />
<CollectionViewSource x:Key="devicesViewSource" Source="{Binding Path=Devices, Source={StaticResource devicesDataSet}}" />
</Window.Resources>
<Grid DataContext="{StaticResource devicesViewSource}">
<ListBox Height="237" HorizontalAlignment="Left" Margin="12,12,0,0" Name="listBox1"
VerticalAlignment="Top" Width="254" ItemsSource="{Binding}" SelectedValuePath="Selected">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Make}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Margin="223,267,0,0" Text="{Binding Path=Make, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
Whenever a device is selected and a property is edited (I'm only displaying one property at the moment), the listbox is updated, but the dataset (database?) doesn't seem to be. That is, when I leave the form and then come back to it, the listbox returns to its original state.
So I guess the question is: how do I make these changes persist/write to the database?
Edit: Derp, here's the updated backend C#:
using System.Windows;
using System.Data;
namespace Inventory
{
public partial class SignOutDevice : Window
{
DevicesDataSet devicesDataSet = null;
Inventory.DevicesDataSetTableAdapters.DevicesTableAdapter devicesDataSetDevicesTableAdapter = null;
public SignOutDevice()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
devicesDataSet = ((Inventory.DevicesDataSet)(this.FindResource("devicesDataSet")));
devicesDataSetDevicesTableAdapter = new Inventory.DevicesDataSetTableAdapters.DevicesTableAdapter();
devicesDataSetDevicesTableAdapter.Fill(devicesDataSet.Devices);
System.Windows.Data.CollectionViewSource devicesViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("devicesViewSource")));
devicesViewSource.View.MoveCurrentToFirst();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
devicesDataSet.AcceptChanges();
devicesDataSetDevicesTableAdapter.Update(devicesDataSet.Tables["Devices"].Select(null, null, DataViewRowState.Deleted));
devicesDataSetDevicesTableAdapter.Update(devicesDataSet.Tables["Devices"].Select(null, null, DataViewRowState.ModifiedCurrent));
devicesDataSetDevicesTableAdapter.Update(devicesDataSet.Tables["Devices"].Select(null, null, DataViewRowState.Added));
}
}
}
Have you looked in the Output window to check for binding errors? Because it looks to me like you have one. The TextBox is bound to the Make property, but its DataContext is the devicesViewSource resource. There's nothing associating it with the selected item in the ListBox.
A way to associate the two would be to assign the ListBox a name, and then set the binding on the TextBox to {Binding ElementName=MyListBox, Path=Make, Mode=TwoWay}.
Okay, figured it out. Turns out the auto-generated Update() method in devicesDataSetDevicesTableAdapter didn't actually do anything. Made a custom method with the Query Wizard and now all is well.