I want to set a property in my ViewModel with the value of the parent Treeview node, but can't get the right way.
Here is the code:
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (e.NewValue.GetType() == typeof(Customer))
{
this.MainViewModel.SelectedCustomer = (Customer)e.NewValue;
this.MainViewModel.FinishConSiteCommand.RaiseCanExecuteChanged();
}
else
{
this.MainViewModel.SelectedConstructionSite = (ConstructionSite)e.NewValue;
// here I want to set "this.MainViewModel.SelectedCustomer" this is the parent node.
this.MainViewModel.FinishConSiteCommand.RaiseCanExecuteChanged();
}
}
And the XAML
<TreeView x:Name="treeView" ItemsSource="{Binding ConstructionSiteDictionary.Values}" SelectedItemChanged="TreeView_SelectedItemChanged">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding ConstructionSites}">
<Label Content="{Binding DisplayableInfos}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView
Can anyone help me? Thanks! :)
Okay I found a solution for my own question:
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (e.NewValue.GetType() == typeof(Customer))
{
this.MainViewModel.SelectedCustomer = (Customer)e.NewValue;
this.MainViewModel.FinishConSiteCommand.RaiseCanExecuteChanged();
}
else
{
this.MainViewModel.SelectedConstructionSite = (ConstructionSite)e.NewValue;
Customer customer = null;
foreach (Customer c in this.MainViewModel.ConstructionSiteDictionary.Values)
{
foreach (ConstructionSite site in c.ConstructionSites)
{
ConstructionSite selecetedSite = (ConstructionSite)e.NewValue;
if (site == selecetedSite)
customer = c;
}
}
this.MainViewModel.SelectedCustomer = customer;
this.MainViewModel.FinishConSiteCommand.RaiseCanExecuteChanged();
}
}
This works fine, but the runtime complexity isn't very well, when ConstructionSiteDictionary has a lot of data. Has anyone a better solution?
Related
I have this LongListSelector in a page:
<Controls:LongListSelector Height="Auto" x:Name="historylist" HorizontalContentAlignment="Stretch"
Background="Black" SelectionChanged="DidPressSelectItem">
<Controls:LongListSelector.ItemTemplate>
<DataTemplate>
<local:SearchTemplateSelector Content="{Binding}" HorizontalContentAlignment="Stretch">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu Opened="ContextMenu_Opened">
<toolkit:MenuItem Header="Edit" Click="EditVideo"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
.
.
.
.
</local:SearchTemplateSelector>
</DataTemplate>
</Controls:LongListSelector.ItemTemplate>
And this is the EditVideo
private void EditVideo(object sender, RoutedEventArgs e)
{
VideoItem selectedVideo = (sender as MenuItem).DataContext as VideoItem;
if (video == null) { return; }
//Do Stuff
this.RelodeTableData();
}
And the RelodeTableData:
private void RelodeTableData()
{
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
{
searchResults.Clear();
for (int i = 0; i < historyRep.historyArray.Count; i++)
{
VideoItem item = historyRep.historyArray[i];
searchResults.Add(item);
}
});
}
And the problem is that when the user edit a item and try to edit another item after that he get the last item he edit in selectedVideo.
I use the ReloadTableData to refresh the list data after i edit it.
Ok after a lot of searching how to fix this thing. i fount that if i add Unload method to the ContextMenu and this is clear the DataContext.
private void ContextMenu_Unload(object sender, RoutedEventArgs e)
{
ContextMenu conmen = (sender as ContextMenu);
conmen.ClearValue(FrameworkElement.DataContextProperty);
}
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);
I'm trying to create a new TreeViewItem with a control in it like:
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel>
<Button/>
</StackPanel>
<TreeViewItem.Header>
<TreeViewItem>
Except, I'd like to do it at runtime (I'm using C#), but I can't work out how to do this. Can you help?
This is my code that I'm using to generate the node. Somewhere in here I would like to insert a numeric up/down control. I don't have that control yet, but for arguement's sake, let's say that I want to insert a button.
private void TreeView_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
{
if (TreeView.SelectedNode != null)
{
if (((vcvscompiler.DataTypes.dataObjectv)(TreeView.SelectedNode.Tag))._vcardName.re == "adr_work")
{
foreach (string k in ((vcvscompiler.DataTypes.dataObjectv)(TreeView.SelectedNode.Tag))._prefs)
{
TreeViewItem newChild = new TreeViewItem();
newChild.Header = k;
treeView1.Items.Add(newChild);
}
}
}
}
WPF:
<Window.Resources>
<DataTemplate x:Key="myTaskTemplate">
<StackPanel>
<Button content="This is a button!" />
</StackPanel>
</DataTemplate>
</Window.Resources>
new TreeViewItem {
Header = new StackPanel {
Children = {
new Button { ... }
}
}
}
I hoped the answer to my previous question whould help me with this one, but it didn't. The initial situation is pretty much the same:
<TreeView ItemsSource="{Binding Groups}" Name="tvGroups" AllowDrop="True">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Participants}">
<StackPanel Orientation="Horizontal" Drop="tvDrop" Tag="{Binding .}">
<TextBlock Text="{Binding Name}" />
<Button Tag="{Binding .}" Click="Button_Click_2">
<Image Source="Resources/cross.png" />
</Button>
</StackPanel>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding Alias}" />
<Button Tag="{Binding .}" Name="btnDeleteParticipants" Click="btnParticipants_Click" >
<Image Source="Resources/cross.png" />
</Button>
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
private void btnParticipants_Click(object sender, RoutedEventArgs e)//Probanten aus Gruppe entfernen
{
Participant p = ((sender as Button).Tag as Participant);
if (p == null) return;
//TODO: Raus bekommen in welcher Gruppe ich löschen will
}
I want to remove Participant p from a Group by clicking the button(btnDeleteParticipants). I tried something like this one:
Control c = sender as Control;
while (!(c is TreeViewItem))
c = (c.Parent) as Control;
But this didn't work (don't ask why, I'm not sure). I could find the Group by checking if it contains the Participant(bound to btnDeleteParticipants.Tag), but this would disallow participants to be in more than 1 group.
So, any ideas how to get the right Group?
Edit:
Groups = new ObservableCollection<Group>();
Participants = new ObservableCollection<Participant>();
Are Groups and Participants ObservableCollection objects?
Try using this:
static TObject FindVisualParent<TObject>(UIElement child) where TObject : UIElement
{
if (child == null)
{
return null;
}
UIElement parent = VisualTreeHelper.GetParent(child) as UIElement;
while (parent != null)
{
TObject found = parent as TObject;
if (found != null)
{
return found;
}
else
{
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
}
return null;
}
Also, try using the DataContext to get the participant and set the Tag to the TemplatedParent.
<Button Tag="{Binding RelativeSource={RelativeSource TemplatedParent}}" />
Then on click
private void btnParticipants_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
var p = button.DataContext as Participant;
if (p == null) return;
var t= FindVisualParent<TreeViewItem>(button); // get the Participants TreeViewItem
if (t == null) return;
var groupTreeItem = FindVisualParent<TreeViewItem>(t); // get the groups TreeViewItem
if (groupTreeItem == null) return;
var group = groupTreeItem.DataContext as Group;
group.Participants.Remove(p);
}
i have a listbox with a data template that contains a button.
When the button is clicked I want to get in the button
click handler the index of the listbox item that was current??
How do I do this please?
Malcolm
More appropriate answer,
private void Button_Click(object sender, RoutedEventArgs e)
{
DependencyObject dep = (DependencyObject)e.OriginalSource;
while ((dep != null) && !(dep is ListViewItem))
{
dep = VisualTreeHelper.GetParent(dep);
}
if (dep == null)
return;
int index = lstBox.ItemContainerGenerator.IndexFromContainer(dep);
}
Hope the bellow code will help you.
private void Button_Click(object sender, RoutedEventArgs e)
{
var b = (Button)sender;
var grid = (Grid)b.TemplatedParent
var lstItem = (ListBoxItem)grid.TemplatedParent;
int index = lstBox.ItemContainerGenerator.IndexFromContainer(lstItem);
// rest of your code here...
}
And the XAML for the above assumed to be a DataTemplate on a ListBox named lstBox:
<DataTemplate x:Key="template">
<Grid>
<Button Click="Button_Click" Content="Press"/>
</Grid>
</DataTemplate>
Have you checked the "SelectedIndex" property of the listbox? It might be set by clicking on the button.
Probably way to late but using "IndexOf" on the listbox "Items" will give you the index #
Regards
myListbox.Items.CurrentItem seems be what you are looking for.
Hi you can use ContentPresenter.Content to get current item , instead of current index:
<DataTemplate DataType="{x:Type MyModel}">
<StackPanel Orientation="Horizontal" Margin="0 5">
<TextBlock Text="{Binding Title}" />
<Button Content="Active" Click="Button_Click" />
</StackPanel>
</DataTemplate>
and in code:
private void Button_Click(object sender, RoutedEventArgs e)
{
var button = e.Source as Button;
var contentPresenter = button.TemplatedParent as ContentPresenter;
var myModel = (MyModel)contentPresenter.Content;
}