I created a ListPickerFlyout and I would like, through a button, select an item from the list.
In XAML I have done so:
<Button x:Name="BottoneFiltraCittaNotizie" Click="BottoneFiltraCittaNotizie_Click" Style="{StaticResource ButtonSearchStyle}" Grid.Row="0" BorderBrush="{x:Null}" Foreground="Gray" Margin="0,-12,0,0">
<Button.Flyout>
<ListPickerFlyout ItemsSource="{Binding Source={StaticResource Museum}}">
<ListPickerFlyout.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding NomeProvincia}" HorizontalAlignment="Left"/>
</StackPanel>
</DataTemplate>
</ListPickerFlyout.ItemTemplate>
</ListPickerFlyout>
</Button.Flyout>
</Button>
in c # I want to recover the selected item, and then do some operations. MSDN there is SelectedItem instead I can not find it, and I said that does not exist, how could I do?
private void BottoneFiltraCittaNotizie_Click(object sender, RoutedEventArgs e)
{
Regioni region = ListPickerFlyout.SelectedItem as Regioni; //ERROR!!
string regione = region.NomeRegione;
var GruppiAllNEWS = NotizieFB.Where(x => x.TAG.Contains(regione)).OrderBy(x => x.Data).Reverse();
}
Add a property of your object to your view model (or code behind, or whatever you use as your datacontex), and then add a binding to that on that list.
Assuming you have public MyObject my_object {get;set;} in your datacontext, you should have on your xaml something like:
<ListPickerFlayout ...
SelectedItem = {binding my_object;} />
This way it knows that whatever is selected, will be that object in the data context, and you can access it from your code, by simply using the above property:
public class SomeClass {
// this is your code behind file
public MyObject my_object {get;set;}
// this is where you go when you hit the button
OnButtonClick(sender, event) {
//my_object is accessible here. Assuming it has a DoSomething method, you can:
my_object.DoSomething();
}
}
Alternatively, as this msdn suggests, you won't have to bind to a property (i would, since i'll try to do it in a MVVM way), and all you'll have to do is cast the sender and use it's selected item, something along:
void PrintText(object sender, SelectionChangedEventArgs args)
{
// get your object with the cast, and then get it's item
ListBoxItem lbi = ((sender as ListBox).SelectedItem as ListBoxItem);
// then you can use it like:
tb.Text = " You selected " + lbi.Content.ToString() + ".";
}
Related
I created a textblock on a XAML form within a ContentControl. When I try to program it, C# doesn't recognize the name and I can't do anything with it.
I tried adding a textblock to the form outside of the Content Control, but that still didn't fix the problem.
Here is the XAML code:
<ContentControl>
<ContentControl.Template>
<ControlTemplate>
<Grid VerticalAlignment="Bottom" Height="250" Margin="0,450,0,0">
<Rectangle Fill="Beige" Stroke="Black" StrokeThickness="3"
Width="639" Height="250" Margin="0,0,0,0"/>
<TextBlock Text="Goal:" FontSize="18" Margin="7,50,0,0"/>
<TextBlock Text="Eaten:" FontSize="18" Margin="7,120,0,0"/>
<TextBlock Text="Remaining:" FontSize="18" Margin="7,190,0,0"/>
<TextBlock Text="Calories:" FontSize="18" Margin="140,10,0,0"/>
<TextBlock Text="Fat(g):" FontSize="18" Margin="270,10,0,0"/>
<TextBlock Text="Carbs(g):" FontSize="18" Margin="380,10,0,0"/>
<TextBlock Text="Protein(g):" FontSize="18" Margin="520,10,0,0"/>
<TextBlock x:Name="lblCalorieGoal" Text="Peb"
TextAlignment="Center" FontSize="18" Margin="-290,50,0,0"/>
</Grid>
</ControlTemplate>
</ContentControl.Template>
<TextBlock Text="TextBlock" TextWrapping="Wrap"/>
</ContentControl>
And then here is the corresponding working C# code:
public LogFood()
{
this.InitializeComponent();
Windows.Storage.ApplicationDataContainer localSettings =
Windows.Storage.ApplicationData.Current.LocalSettings;
Windows.Storage.StorageFolder localFolder =
Windows.Storage.ApplicationData.Current.LocalFolder;
Windows.Storage.ApplicationDataCompositeValue composite =
(Windows.Storage.ApplicationDataCompositeValue)localSettings
.Values["nutritionSettings"];
int calorieMin = Convert.ToInt32(composite["calorieMin"]);
int calorieMax = Convert.ToInt32(composite["calorieMax"]);
int gramsFatMin = Convert.ToInt32(composite["gramsFatMin"]);
int gramsFatMax = Convert.ToInt32(composite["gramsFatMax"]);
int gramsCarbsMin = Convert.ToInt32(composite["gramsCarbsMin"]);
int gramsCarbsMax = Convert.ToInt32(composite["gramsCarbsMax"]);
int gramsProteinMin = Convert.ToInt32(composite["gramsProteinMin"]);
int gramsProteinMax = Convert.ToInt32(composite["gramsProteinMax"]);
lblCalorieGoal.Text = calorieMin;
}
I expect to be able to change the text of the textblock. Instead, I get the error, "The name lblCalorieGoal.Text does not exist in the current context."
The key realization here is that a template is potentially a reusable part of XAML, so anything inside is in fact embedded in it a not "publicly" accessible, as there could potentially be multiple instances of the same template materialized on the view.
That being said, you can still access the materialized children inside the template indirectly by searching for them within the template using VisualTreeHelper -
internal static FrameworkElement FindChildByName(DependencyObject startNode, string name)
{
int count = VisualTreeHelper.GetChildrenCount(startNode);
for (int i = 0; i < count; i++)
{
DependencyObject current = VisualTreeHelper.GetChild(startNode, i);
if (current is FrameworkElement frameworkElement)
{
if (frameworkElement.Name == name)
return frameworkElement;
}
var result = FindChildByName(current, name);
if ( result != null)
{
return result;
}
}
return null;
}
Note, that this works only after the control has loaded (for example in the Page.Loaded event handler -
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var block = FindChildByName(ContentRoot, "lblCalorieGoal") as TextBlock;
}
However, this all is not an ideal solution to your problem. Instead, you should either ditch the use of ContentControl altogether and have the controls in the template directly on the page (which would make them directly accessible from the code-behind), or/and use data-binding to bind data directly to appropriate controls. In this case, I would create a class to hold the data, for example:
public class NutritionInfo
{
public string CalorieGoal { get; set; }
}
Now instead of ContentControl.ControlTemplate (which replaces the template of the whole control), you will replace the ContentTemplate instead (which is just the thing which `ControlTemplate in fact displays):
<ContentControl x:Name="ContentRoot">
<ContentControl.ContentTemplate>
<DataTemplate x:DataType="local:NutritionInfo">
... your template
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
Note we use x:DataType to specify the type we bind to so that we can use x:Bind syntax. Finally, we update the template itself:
<TextBlock x:Name="lblCalorieGoal" Text="{x:Bind CalorieGoal}" ... />
We use x:Bind to bind the text of the TextBlock to the CalorieGoal property. We are almost done, now just set the Content property of the ContentControl to an instance of NutritionInfo (for example via data binding or directly):
ContentRoot.Content = new NutritionInfo()
{
CalorieGoal = "1243"
};
Overall I recommend to read further about how data-binding works in XAML, as that will help you significantly simplify your code and avoid accessing controls directly via x:Name, and decouple UI from your code. See documentation for more info.
i want to change textblock text in page initialize event
here is my xaml
<ListBox Margin="3,60,1,10" BorderThickness="2" Grid.Row="1" Name="lstAnnouncement" Tap="lstAnnouncement_Tap" Width="476" d:LayoutOverrides="VerticalMargin">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Name="thispanel" Grid.Row="1" Orientation="Horizontal" Height="120" Width="478" >
<StackPanel.Background>
<ImageBrush ImageSource="Images/Text-ALU.png" Stretch="Fill" />
</StackPanel.Background>
<Grid HorizontalAlignment="Left" Width="30" Margin="0,0,0,2" Background="#FF0195D5" Height="118">
<TextBlock x:Name="txtDate" TextWrapping="Wrap">
</TextBlock>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
i want to change txtDate.Text using c# in code-behind but txtdate is not accessible in code behind so how to achieve it ?
The reason you're not able to access the txtDate object is because it's contained within the DataTemplate you're using for the ListBox. This isn't an error - the DataTemplate is being applied to every single item added to your ListBox.
Given that the ListBox creates, among other controls, a Grid containing a TextBlock with the name "txtDate", for every single item added to it, what would it mean to access the txtDate object? How would your program decide which of a (functionally) infinite number of txtDates associated with an identical number of ListBoxItems you meant when you referenced txtDate?
If you wanted to be able to easily change the content of txtDate, you'd want to bind the ItemsSource of your ListBox to a property in a ViewModel. The easiest way to do this would be to have that property be an IEnumerable containing a custom model type. This way, you could update the text property of that model and call NotifyPropertyChanged on the that property, and the UI would update to reflect the new data.
Here's an example:
public class YourViewModel
{
public List<YourModel> Models { get; set; }
}
public class YourModel : INotifyPropertyChanged
{
private string yourText;
public string YourText
{
get { return yourText; }
set
{
yourText = value;
NotifyPropertyChanged("YourText");
}
}
// add INotifyPropertyChanged implementation here
}
And then you'd want to bind the ItemsSource of the ListBox to YourViewModel's Models property, and the text of your TextBox to the YourModel's YourText property. Any time you change the YourModel.YourText property, it'll automatically update on the UI. I think it's probably subject to debate whether having your model implement INotifyPropertyChanged is proper MVVM, but I find it a lot easier in these cases than forcing the ViewModel to update every single model each time a change is made on one of them.
If you're not familiar with the MVVM pattern used with WPF, this might be a good start: MVVM example.
this function will help you... This will help u find the control inside of a listbox runtime..
public FrameworkElement SearchVisualTree(DependencyObject targetElement, string elementName)
{
FrameworkElement res = null;
var count = VisualTreeHelper.GetChildrenCount(targetElement);
if (count == 0)
return res;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(targetElement, i);
if ((child as FrameworkElement).Name == elementName)
{
res = child as FrameworkElement;
return res;
}
else
{
res = SearchVisualTree(child, elementName);
if (res != null)
return res;
}
}
return res;
}
Here first parameter is parent and the second parameter is the name of the element which in your case is "txtDate".. hope it works!!
I have a ListBox with binding, when i add Items it works perfect but if i try to remove the items with contextMenu it doesnt work.
Here is what i try so far ListBox Xaml Code
<ListBox Name="lstPersons"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" Margin="126,-228,2,-242">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu Name="PersonContext">
<toolkit:MenuItem Name="PersonDelete" Header="Delete" Click="DeletePerson_Click"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Name="btnKellnerName"
Text="{Binding _PersonName}"
FontSize="35"
FontFamily="Portable User Interface"/>
<TextBlock Name="btnPosition"
Text="{Binding _PersonPosition}"
FontSize="22"/>
<TextBlock Name="lblUhrzeit"
Text="{Binding _CreationDate}"
FontSize="18"/>
<TextBlock Name="Space" Text=" "/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And the Binding Class Code
public class Person
{
public string _PersonName { get; set; }
public string _PersonPosition { get; set; }
public string _CreationDate { get; set; }
}
When i add items like this
ObservableCollection<Person> personList = new ObservableCollection<Person>();
personList.Add(new Person {
_PersonName = "Tom",
_PersonPosition = "Bla",
_CreationDate = "33"
});
this.lstPerson.ItemSource = personList;
it works pefect! Now i Want to remove a selected Item with the ContextMenu like this
private void DeletePerson_Click(object sender, RoutedEventArgs e)
{
int indexPerson = lstPerson.SelectedIndex;
personList.RemoveAt(indexPerson);
}
but it doesnt work. Does Anybody have an Idea what im making wrong? Thanks
Ok Guys i have now the Solution the Problem was the value of SelectedIndex now ive got the right Value. First ive put the ContextMenu inside ListBoxItemTemplate/StackPanel
Code Behind:
private void DeletePerson_Click(object sender, RoutedEventArgs e)
{
try {
var selectedListBoxItem = listBox.ItemContainerGenerator.ContainerFromItem(((MenuItem) sender).DataContext) as ListBoxItem;
var selectedIndex = listBox.ItemContainerGenerator.IndexFromContainer(selectedListBoxItem);
_personList.RemoveAt(selectedIndex);
}
catch( Exception ex ) { MessageBox.Show(ex.Message); };
}
In addition to what Joan said, you can also do this in Xaml like this:
<ListBox ItemsSource={Binding personList}
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" Margin="126,-228,2,-242">
<toolkit:ContextMenuService.ContextMenu>
</ListBox>
You should read how to use Bindings and the MVVM-Model. It's pretty convenient for stuff like this.
Here are some links:
http://channel9.msdn.com/Events/MIX/MIX10/EX14
http://channel9.msdn.com/Events/MIX/MIX11/OPN03
Don't get discouraged. It's a bit of learning at the beginning, but it's totally worth it. I also had some problems with displaying lists, before I started doing everything with MVVM using Laurent Bugnions' MvvmLight package.
try this:
private void DeletePerson_Click(object sender, RoutedEventArgs e)
{
System.Collections.IList pathRemove;
pathRemove = lstPerson.SelectedItems;
if(pathRemove.Count != 0)
{
for (int i = pathRemove.Count - 1; i >= 0; i--)
{
lstPerson.Remove((Person)pathRemove[i]);//multiple deletes
}
}
}
Try to add this when the form is initialized:
lstPersons.ItemsSource = personList ;
How to: Create and Bind to an ObservableCollection
It appears that the problem is due to selected item
The key is setting the PreviewMouseRightButtonDown event in the
correct place. As you'll notice, even without a ContextMenu right
clicking on a ListViewItem will select that item, and so we need to
set the event on each item, not on the ListView.
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="PreviewMouseRightButtonDown"
Handler="OnListViewItemPreviewMouseRightButtonDown" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Menu Item">Item 1</MenuItem>
<MenuItem Header="Menu Item">Item 2</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
<ListViewItem>Item</ListViewItem>
<ListViewItem>Item</ListViewItem>
<ListViewItem>Item</ListViewItem>
<ListViewItem>Item</ListViewItem>
<ListViewItem>Item</ListViewItem>
<ListViewItem>Item</ListViewItem>
</ListView>
.
private void OnListViewItemPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
Trace.WriteLine("Preview MouseRightButtonDown");
e.Handled = true;
}
wpf listview right-click problem
Why don't use binding for all.
Bind item source as ObservableCollection blabla
Bind Selecteditem as XXXX
Use command for your button and when you want remove an item do :
blabla.remove(SelectedItem);
I've got for example ListBox with two TextBlocks like this:
<ListBox Name="listboxNews"
SelectionChanged="listboxNews_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Width="400"
Height="70">
<TextBlock Text="{Binding Title}" name="title" />
<TextBlock Text="{Binding Description}" name="desc" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And as you can see, I've got listboxNews_SelectionChanged method, in which i need to select Text of first TextBlock (if posibble by name so it will be independent on order of textblocks), but this one, which I select. For example if first item has title "Item 1" and second "Item 2" and I click on second one, i need to get "Item 2". I was trying something with listboxNews.Items, but i guess this is not correct. Thanks for help.
The SelectedItem property will hold the currently selected object. You can just cast that and take the Title property.
Try this code:
private void listboxNews_SelectionChanged(object sender, SelectionChangedEventArgs e) {
var current = listboxNews.SelectedItem as MyObjectType;
MessageBox.Show(current.Title);
}
Change MyObjectType with the type of your object.
This is a copy and paste out of a working Windows Phone 8 solution.
This was also tested successfully in WPF.
private void ListBox_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
foreach (UIElement item in (sender as ListBox).Items)
{
if ((sender as ListBox).SelectedItem == item)
{
foreach (UIElement InnerItem in (item as StackPanel).Children)
{
if ((InnerItem is TextBlock) && (InnerItem as TextBlock).Name.Equals("title"))
{
MessageBox.Show((InnerItem as TextBlock).Text);
}
}
}
}
}
I'm having a problem with getting a string from a bound textblock within a listbox, when I use the code below, I can bind the listbox and the listbox has items showing up, but when the item in the list is clicked I don't get the proper string, I print a message box a message with objects names like
"MyApp.Item"
shows up instead. myApp is the name of the app and Item is the name of my model that I am binding to the listbox. The proper text from the selected item showed up when the listbox was not binded.
private void listBoxtrend_Tap(object sender, GestureEventArgs e)
{
selectedText = "";
selectedText = listBox.SelectedValue.ToString();
MessageBox.Show(selectedText);
}
xml
<ListBox ItemsSource="{Binding Item}" Foreground="RoyalBlue"
Height="395" HorizontalAlignment="Center"
Margin="12,111,0,0" Name="listBox"
VerticalAlignment="Top" Width="438"
TabIndex="10" Tap="listBox_Tap" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap" FontSize="26" HorizontalAlignment="Left"
Name="tblItem" Text="{Binding ItemString}"
VerticalAlignment="Top" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'd really appreciate if you could help me thanks
You're binding to the ItemString in the DataTemplate's TextBlock and the Item Collection in the ListView. As such the SelectedValue will be of the Item type. You should actually be doing something like this in your Tap handler to get at the ItemString's value...
private void listBoxtrend_Tap(object sender, GestureEventArgs e)
{
selectedText = "";
var selected = listBox.SelectedValue as Item;
selectedText = selected.ItemString;
MessageBox.Show(selectedText);
}
In your example, the ToString is printing the name of the class. You could also override ToString in your Item model to be whatever you want the string to be.
Note: the types and such may be a bit off, I guessed a bit based off of what you wrote in your question. Also, there is no need to set selectedText to an empty string that will just be overwritten in the third line above. I wanted to keep it so you could get some idea of what I changed in your code.
It's very simple, try following:
string selectedText = ListBox.GetItemText(ListBox.SelectedItem);
You need to also set the SelectedItem of the Listbox to something.
SelectedItem = {Binding SelectedItem}
and rename your ItemsSource to "Items" as that makes more sense.
Your SelectedItem in your codebehind or your ViewModel should then contain a property:
public class Item
{
public string ItemString { get;set; }
}
Try This...
string ListBoxConent = ((ListBoxItem)listbox.SelectedItem).Content.ToString();
Try
listBox.SelectedItem.ToString()
If a property isn't specified in ValueMember then SelectedValue returns the results of the ToString method of the object.