In a HierarchicalDataTemplate Label transforms to TextBlock - c#

While trying to implement Drag&Drop in my application I've encountered some strange behavior. I am using a HierarchicalDataTemplate to represent my data structure in a TreeView like so:
XAML:
<TreeView Name="treeViewNotes" AllowDrop="True" PreviewMouseLeftButtonDown="treeViewNotes_PreviewMouseLeftButtonDown" PreviewMouseMove="treeViewNotes_PreviewMouseMove" Drop="treeViewNotes_Drop" DragEnter="treeViewNotes_DragEnter" SelectedItemChanged="treeViewNotes_SelectedItemChanged">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:NoteCategory}" ItemsSource="{Binding Notes}">
<StackPanel Orientation="Horizontal">
<Image Height="16" Source="{Binding TreeViewIcon}" Tag="{Binding Self}"/>
<Label Content="{Binding Title}" Tag="{Binding Self}" Margin="3"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:TextNote}">
<StackPanel Orientation="Horizontal">
<Image Height="16" Source="{Binding TreeViewIcon}" Tag="{Binding Self}"/>
<Label Content="{Binding Title}" Tag="{Binding Self}" Margin="3"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
CODE:
private void buttonCreateNote_Click(object sender, RoutedEventArgs e)
{
ObservableCollection<NoteCategory> NoteCategoriesList = new ObservableCollection<NoteCategory>();
NoteCategory category1 = new NoteCategory("Category 1", Properties.Resources.FolderIcon);
TextNote textNote1 = new TextNote("Text note 1", Properties.Resources.TextNoteIcon, category1, "Blah blah. Content for note 1");
TextNote textNote2 = new TextNote("Text note 2", Properties.Resources.TextNoteIcon, category1, "Blah blah. Content for note 2");
NoteCategory category2 = new NoteCategory("Category 2", Properties.Resources.FolderIcon);
TextNote textNote4 = new TextNote("Text note 4", Properties.Resources.TextNoteIcon, category2, "Blah blah. Content for note 4");
TextNote textNote5 = new TextNote("Text note 5", Properties.Resources.TextNoteIcon, category2, "Blah blah. Content for note 5");
NoteCategoriesList.Add(category1);
NoteCategoriesList.Add(category2);
treeViewNotes.ItemsSource = NoteCategoriesList;
}
RESULT:
This works great, so now I want to be able to drag&drop notes between categories, so I implemented partially the code from here
CODE:
private void treeViewNotes_PreviewMouseMove(object sender, MouseEventArgs e)
{
Point mousePos = e.GetPosition(null);
Vector diff = Utils.DragDropStartPoint - mousePos;
if (Utils.IsDragDropping == false &&
(e.LeftButton == MouseButtonState.Pressed || e.RightButton == MouseButtonState.Pressed) &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance || Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
{
// Get the dragged ListViewItem
TreeView treeView = sender as TreeView;
if (e.OriginalSource is Image || e.OriginalSource is Label)
{
FrameworkElement elem = e.OriginalSource as FrameworkElement;
if (elem.Tag == null)
MessageBox.Show(elem.GetType() + "\n\nNULL");
else
MessageBox.Show(elem.GetType() + "\n\n" + elem.Tag.ToString());
}
else
MessageBox.Show(e.OriginalSource.GetType().ToString());
}
}
The key to the whole operation is binding the Tag to the TextNote object itself, so I can extract it when the user drags either the Label or Image of the TreeViewItem. And it works for the Image. But when I try to drag by clicking the Label the e.OriginalSource actually comes to the handler as a TextBlock with an empty Tag. I couldn't make the Label fire the event no matter where I click and drag the mouse. The question is why?
I did a workaround by using a TextBlock instead of a Label in the HierarchicalDataTemplate and it works, but I am still curious to why the other approach does not work.

Related

Reach a TextBlock from a specific ListViewItem from the ListView in Windows Phone 8.1 XAML programmatically

I am a new developer on Windows Phone 8.1, I am try to reach a specific ListView item from the ListView collection and be able to color it or color the TextBock inside of it, But I can't reach the item or reach any of items inside of ListView, Please take a look for my below code :
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
SQLiteRT db1 = new SQLiteRT();
var db_connection = await db1.Connection("MyDB.sqlite");
List<MyTBL> t_list = db1.GetTable("SELECT * FROM MyTBL LIMIT 4 ORDER BY RANDOM() ;");
db_connection.Close();
LV_Options.ItemsSource = t_list;
}
// my List View called LV_Options
private void LV_Options_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListView lv1 = sender as ListView;
if (lv1 == null)
return;
MyTBL wrd = lv1.SelectedItem as MyTBL;
if (wrd == null)
return;
TextBlock tb = lv1.FindName("TB_AMean1") as TextBlock;
tb.FontSize = 17; // here I got debug error (it not worked !!!!!!!)
var item = LV_Options.Items.ElementAt(3); // this seems not work also !!!!
item.BackColor = Color.LightSteelBlue;
}
As you can see above, I tried to reach a specific item by LV_Options.Items.ElementAt(3) but it doesn't work! I also tried to reach the TextBlock from the selected List view item, but also not worked !
(Updated)
XAML code :
<!-- Title Panel -->
<StackPanel Grid.Row="0" Margin="19,0,0,0">
<TextBlock Name="TB_Rslt" Text="Here result of your answer" Style="{ThemeResource TitleTextBlockStyle}" Margin="0,12,0,0"/>
<TextBlock Text="page title" Margin="0,-6.5,0,26.5" Style="{ThemeResource HeaderTextBlockStyle}" CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"/>
</StackPanel>
<!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,10,19,15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Name="TB_Question" Text="Choose Answer " Margin="0,0,25,0" HorizontalAlignment="Right" FontWeight="Bold" FontSize="22" FontFamily="Verdana" RenderTransformOrigin="0.5,0.5" />
<TextBlock Name="TB_EnWord" Text="" Margin="90,0,15,0" HorizontalAlignment="Left" FontWeight="Bold" FontSize="22" FontFamily="Verdana" RenderTransformOrigin="0.5,0.5" TextAlignment="Right" />
<StackPanel Grid.Row="1" Margin="5,22,0,0">
<ListView Name="LV_Options" SelectionChanged="LV_Options_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="6">
<StackPanel VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Name="TB_AMean1" Text="{Binding AMean1}" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<Button Name="Btn_Answer" Content="Ansewr" HorizontalAlignment="Left" Grid.Row="1" VerticalAlignment="Bottom" Click="Btn_Answer_Click"/>
My application is a quiz application that offer 4 choices/options as answers for each question, and when user select a true answer, I want to highlight the true answer(true choice) by make its background to green, and if the user selected wrong answer/option I want to make the background of that answer (a specific List View item) with red.
Any help please ?
You're not going to be able to access an element inside a data template like that. Instead, leverage the binding to a view model to set the color and other view-related properties. First, create a wrapper view model for your data class:
public class MyTBLViewModel : INotifyPropertyChanged
{
public MyTBL Entity
{
get { return _entity; }
}
private readonly MyTBL _entity;
public Brush Highlight
{
get { return _brush; }
set
{
_brush = value;
RaisePropertyChanged("Highlight");
}
}
private Brush _highlight;
public double ItemFontSize
{
get { return _itemFontSize; }
set
{
_itemFontSize = value;
RaisePropertyChanged("ItemFontSize");
}
}
private Brush _itemFontSize;
public MyTBLViewModel(MyTBL entity)
{
_entity = entity;
_highlight = new SolidColorBrush(Colors.Transparent);
_itemFontSize = 12;
}
public event PropertyChangedEventArgs PropertyChanged;
protected void RaisePropertyChanged(string propName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propName));
}
}
Use this as your ItemsSource:
List<MyTBLViewModel> t_list = db1.GetTable("SELECT * FROM MyTBL LIMIT 4 ORDER BY RANDOM() ;")
.AsEnumerable().Select(entity => new MyTBLViewModel(entity)).ToList();
Now in your view, bind the view elements to "Highlight" and "ItemFontSize", and to any other properties you like:
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="6" Background="{Binding Highlight}">
<StackPanel VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Name="TB_AMean1" Text="{Binding Entity.AMean1}" TextWrapping="Wrap"
FontSize="{Binding ItemFontSize}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Finally, you can get the data item from the SelectionChangedEventArgs -- use it to update your view-related properties:
private void LV_Options_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems.OfType<MyTBLViewModel>())
{
item.Highlight = new SolidColorBrush(Color.LightSteelBlue);
item.ItemFontSize = 17;
}
foreach (var item in e.RemovedItems.OfType<MyTBLViewModel>())
{
item.Highlight = new SolidColorBrush(Colors.Transparent);
item.ItemFontSize = 12;
}
}
var item = LV_Options.Items.ElementAt(3);
This line is incorrect. It will not return you a TextBlock. I don't know what a .BackColor is, and it should not compile. The Items property in a ListView will return you a list of ListViewItems. If you want to access the inside element from a ListViewItem, you'll need to access the ContentTemplateRoot property.
Do not use var ever. It lets you assume that you know the type, whereas if you explicitly typed the declaration you would realize you're doing it wrong.
MyTBL wrd = lv1.SelectedItem as MyTBL;
if (wrd == null)
return;
TextBlock tb = lv1.FindName("TB_AMean1") as TextBlock;
What is a MyTBL type? FindName is only available to framework DependencyObjects so I'm assuming it's a user control? You have to provide a lot more code to show us what you're doing and what you're setting the ListView's ItemsSource and ItemTemplate with and what these errors are and how you have 2 breaking debug errors at once and what the error messages are.
Comprehending runtime error messages is a huge part of being a good developer.

Listpicker tap event in fullscreen mode not working correctly

I'm developing windows phone 8 application.
I'm using ListPicker option.
My Listpicker code
<toolkit:ListPicker x:Name="LPfilter" Foreground="White" BorderThickness="0" Margin="300,0,0,0" Height="80" Width="50" Visibility="Visible">
<toolkit:ListPicker.Background>
<ImageBrush ImageSource="/Assets/Images/filters.png"/>
</toolkit:ListPicker.Background>
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding name}" Visibility="Collapsed" Foreground="Red"/>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<TextBlock Tap="TextBlock_Tap">
<Run Text="{Binding name}"/>
</TextBlock>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</toolkit:ListPicker>
values bind to listpicker using webservice (json format).
Json result comes like this
[
-{
id: "9",
name: "Pizza",
root_id: "4",
level: "1",
},
-{
id: "10",
name: "Fine Dinind",
root_id: "4",
level: "1",
},
-{
id: "11",
name: "Fast Food",
root_id: "4",
level: "1",
},
....
]
c# code for bind values
public void businesscatbind()
{
string bus_caturl = "http://xxxxx.com/Service/filterquery.php?rootid=" + bus_catval;
WebClient bus_catwc = new WebClient();
bus_catwc.DownloadStringAsync(new Uri(bus_caturl), UriKind.Relative);
bus_catwc.DownloadStringCompleted += bus_catwc_DownloadStringCompleted;
}
void bus_catwc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
var bus_catdata = e.Result;
var bus_catvalue = JsonConvert.DeserializeObject<List<bus_catbinddata>>(bus_catdata);
LPfilter.ItemsSource = bus_catvalue;
}
Problem occur in this event
private void TextBlock_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
bus_catbinddata elements = LPfilter.SelectedItem as bus_catbinddata;
int val = LPfilter.SelectedIndex;
filterid = int.Parse(elements.id);
MessageBox.Show(filterid.ToString());
}
My problem
First time i click pizza it's show the value = Id value 9 in msgbox
next i click Fine Dining it's show the value = Id value 9 in msgbox
next i click Fast Food it's show the value =10 in msgbox (10 Is the idvalue of FineDining)
next i click Italian it's show the value = 11 in msgbox (11 is the Idvalue of Fastfood)
previews selected item value is show in alert
Output
How to solve this issue.
Use Listpicker SelectionChanged event instead
<toolkit:ListPicker x:Name="LPfilter" Foreground="White" BorderThickness="0" Margin="300,0,0,0" Height="80" Width="50" Visibility="Visible" SelectionChanged="listPicker_SelectionChanged>
in Xaml page
<toolkit:ListPicker x:Name="listPicker" Foreground="White" BorderThickness="0" Margin="300,0,0,0" Height="80" Width="50" Visibility="Visible" SelectionChanged="listPicker_SelectionChanged" >
<toolkit:ListPicker.Background>
<ImageBrush ImageSource="/Assets/Images/filters.png"/>
</toolkit:ListPicker.Background>
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding name}" Visibility="Collapsed" Foreground="Red"/>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<TextBlock Tap="TextBlock_Tap">
<Run Text="{Binding name}"/>
</TextBlock>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</toolkit:ListPicker>
In .cs File
void listPicker_SelectionChanged(object sender, SelectionChangedEventArgs args)
{
if (this.listPicker.SelectedItems != null && this.listPicker.SelectionMode == SelectionMode.Multiple)
{
for (int i = 0; i < this.listPicker.SelectedItems.Count; i++)
{
string str = ((Items)(this.listPicker.SelectedItems[i])).Name;
if (i == 0)
{
MessageBox.Show("Selected Item(s) is " + str);
}
else
{
//Some Code
}
}
}
else if (this.listPicker.SelectionMode == SelectionMode.Single)
{
MessageBox.Show("Selected Item is " + ((Items)this.listPicker.SelectedItem).Name);
}
}
It seems that the "TextBlock_Tap" event fires up before the "ListPicker" changes its selection.
You can try to use the "SelectionChanged" event of the ListPicker - although it seems that you are not using it because you might want to select the same option twice. In that case, you can add a dummy item to the top of your list saying something like "Select Item" and in the "SelectionChanged" event in the end set the selected item of list picker to your dummy item.
EDIT:
In this method -
void bus_catwc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
var bus_catdata = e.Result;
var bus_catvalue = JsonConvert.DeserializeObject<List<bus_catbinddata>>(bus_catdata);
LPfilter.ItemsSource = bus_catvalue;
}
add a new "bus_catbinddata" to the "var bus_catvalue" - it is a list - insert your dummy itme at index 0. then in selection changed event of list picker, add a condition if(ListPicker.SelectedIndex > 0) and only if this condition is true, the code will execute.
I haven't tried this specifically with ListPicker's full mode item template, but you can try to get corresponding model for tapped TextBlock from DataContext :
private void TextBlock_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
TextBlock textBlock = (TextBlock)sender;
bus_catbinddata elements = textBlock.DataContext as bus_catbinddata;
filterid = int.Parse(elements.id);
MessageBox.Show(filterid.ToString());
}

WP7 ListBox selected item is not changing the color

I have a ListBox in app, it has an image and textbox inside. I want to set 2 colors and 3rd one for selected item.
<ListBox.ItemTemplate>
<DataTemplate x:Name="Template1">
<StackPanel Orientation="Horizontal" >
<Image Width="100" Height="100" Source="{Binding SmallImage}"></Image>
<Grid>
<TextBlock Text="{Binding Caption}" Foreground="{Binding txtColor}"></TextBlock>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
when I'm changing the foreground color, then the selected item doesn't highlights (I kept it by default).
I tried to add an event to ListBox,
private void DList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBoxItem selectedItem = DList.SelectedItem as ListBoxItem;
selectedItem.Foreground = new SolidColorBrush(Colors.Red);
}
but it shows an exception:
NullReferenceException
"Use the "new" keyword to create an object instance"
If you're going to handle the SelectionChanged event, then you might as well use the SelectionChangedEventArgs object:
private void DList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedDataObject = e.AddedItems[0]; // assuming single selection
ListBoxItem selectedItem =
ListBoxName.ItemContainerGenerator.ContainerFromItem(selectedDataObject);
selectedItem.Foreground = new SolidColorBrush(Colors.Red);
}

Preselect Value of Treeview inside combobox

I am looking to implement a treeview inside a combobox. Basically I want it to show as a combobox when collapsed but a treeview inside combo box when expanded. When a user clicks on a node, I want it to show in the collapsed combo box. I have got this working so far.
The problem I am having is that how do I show a default value from c# when this combo box is loaded. Please help guys as I am running out of ideas :)
Thanks in advance.
Data Template
<DataTemplate x:Key="TreeViewExpanded" >
<StackPanel>
<TreeView x:Name="DPointTree" Margin="5" ItemsSource="{Binding Datapoint}"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}}"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Field}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding = "{Binding Path=SelectedFieldName, Mode=TwoWay}" Value = "">
<Setter Property = "Visibility" Value = "Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Here is the Xaml
<ComboBox Name="cmbFieldName" Width="150" Background="White" ItemsSource="{Binding}" SelectedItem="{Binding SelectedFieldName , Mode=TwoWay}" >
<ComboBox.ItemTemplateSelector>
<local:TreeViewSelector/>
</ComboBox.ItemTemplateSelector>
</ComboBox>
Here is the DataSet passed to this.
Datapoint _datapoint2 = new Datapoint();
_datapoint2.Name = "Alpha";
_datapoint2.FieldID.Add("Contains Elements");
_datapoint2.Field.Add("1 Year");
_datapoint2.Field.Add("2 Year");
_datapoint2.Field.Add("3 Year");
Try this:
private void TreeView_Loaded(object sender, RoutedEventArgs e)
{
TreeView areaTreeView = sender as TreeView;
// Expand the treeview
if (areaTreeView != null)
{
ExpandCollapseTreeNodes(areaTreeView, true);
}
}
public static void ExpandCollapseTreeNodes(ItemsControl parentContainer, bool isExpanded)
{
foreach (Object item in parentContainer.Items)
{
TreeViewItem currentContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (currentContainer != null) // && currentContainer.Items.Count > 0
{
//expand the item
currentContainer.IsExpanded = isExpanded;
//if the item's children are not generated, they must be expanded
if (isExpanded && currentContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
{
//store the event handler in a variable so we can remove it (in the handler itself)
EventHandler eh = null;
eh = new EventHandler(delegate
{
//once the children have been generated, expand those children's children then remove the event handler
if (currentContainer.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
ExpandCollapseTreeNodes(currentContainer, isExpanded);
currentContainer.ItemContainerGenerator.StatusChanged -= eh;
}
});
currentContainer.ItemContainerGenerator.StatusChanged += eh;
}
//otherwise the children have already been generated, so we can now expand those children
else
{
ExpandCollapseTreeNodes(currentContainer, isExpanded);
}
}
}
}
it is not pretty but works to select a node in the tree
// in code behind after the tree is declared, e.g. after InitializeComponent();
SynchronizationContext.Current.Post(delegate
{
// expand the tree to the selected node after loading
treeView.ExpandAll(); // or if path is known treeView.ExpandPath(...);
SynchronizationContext.Current.Post(delegate
{
treeView.GetContainerFromItem(root.Children[0]).IsSelected = true;
}, null);
}, null);

Enumerate over each item of listbox to compare internal element values

I have a listbox with a data template bound to a list<class> in the program.
<DataTemplate x:Key="pTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Ref}" Padding="5,0,0,0"/>
<StackPanel Name="taggedA" Tag="{Binding A}" Orientation="Horizontal">
<TextBlock Name="selectedA" Text="{B}" />
</StackPanel>
<Image Name="ind" Width="40" Height="40" />
</StackPanel>
</DataTemplate>
On button click, I want to go over all the elments of the listbox and check if the stackPanel taggedA's tag == textblock selectedA's text.
This is to be done for each of the items in the list box and the data template is as above. How can this be done?
Easier to compare the binding source directly:
ListBox l = myListBox;
for (int i = 0; i < l.Items.Count; i++)
{
var boundObject = (MyClass)l.Items[i];
MessageBox.Show("They are equal? " + (boundObject.A == boundObject.B));
}
I would agree with #dbaseman. But if you are set on doing it you could do the following:
private void button_click(object sender, RoutedEvent e)
{
foreach(var item in MyListBox.Items)
{
ListBoxItem lbi = MyListBox.ItemContainerGenerator.ContainerFromItem(item);
StackPanel taggedApanel = (lbi.Content as StackPanel).Children[1];
//Do whatever you need to do here
}
}

Categories