I'm trying to let the user to design his own form and save the controls positions into a DB.
Right now I was able to allow the user to spawn new controls and move them around the form. What I dont know is how to get the position of the controls to my Datacontext. I was able to only bind the width etd...
I was hoping I could bind the canvas.left & canvas.top to datacontext but those are not updated on the renderTransform.
Any Ideas?Thanks for help.
Heres the form back code for moving the controls:
private Control _currentlyDragged;
private Point _currentlyDraggedMouseOffset;
private void Window_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (_currentlyDragged != null)
{
var mousePos = e.GetPosition(this);
var futurePos = e.GetPosition(BuildCanvas);
if (futurePos.X <= 0 || futurePos.Y <= 0 || futurePos.Y >= BuildCanvas.ActualHeight || futurePos.X >= BuildCanvas.ActualWidth)
return;
_currentlyDragged.RenderTransform = new TranslateTransform(mousePos.X - _currentlyDraggedMouseOffset.X, mousePos.Y - _currentlyDraggedMouseOffset.Y);
}
}
private void Window_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_currentlyDragged != null)
_currentlyDragged = null;
ReleaseMouseCapture();
}
private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Point pt = e.GetPosition((UIElement)sender);
_hitResultsList.Clear();
VisualTreeHelper.HitTest(this, null, new HitTestResultCallback(MyHitTestResult), new PointHitTestParameters(pt));
if (!_hitResultsList.Where(h => h is Border && ((Border)h).Name == "BuildCanvas").Any())
return;
if (_hitResultsList.Count > 0)
{
foreach (DependencyObject d in _hitResultsList)
{
var parent = VisualTreeHelper.GetParent(d);
if (parent != null && (parent is Label || parent is TextBox))
{
CaptureMouse();
_currentlyDragged = parent as Control;
if (_currentlyDragged.RenderTransform is TranslateTransform)
{
_currentlyDraggedMouseOffset.X = e.GetPosition(this).X - ((TranslateTransform)_currentlyDragged.RenderTransform).X;
_currentlyDraggedMouseOffset.Y = e.GetPosition(this).Y - ((TranslateTransform)_currentlyDragged.RenderTransform).Y;
}
else
{
_currentlyDraggedMouseOffset.X = pt.X;
_currentlyDraggedMouseOffset.Y = pt.Y;
}
return;
}
}
}
_currentlyDragged = null;
}
private HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
_hitResultsList.Add(result.VisualHit);
return HitTestResultBehavior.Continue;
}
Heres the ItemsControl:
<Border x:Name="BuildCanvas" Grid.Column="1" Grid.Row="0" Background="#fff4c9" CornerRadius="10">
<Grid>
<!-- Generated controls -->
<ItemsControl ItemsSource="{Binding TextBoxCollection}" Panel.ZIndex="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox HorizontalAlignment="Left" VerticalAlignment="Top" Padding="2" IsEnabled="False" Background="White"
Text="{Binding Name, Mode=OneWay}" Width="{Binding Width}">
</TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- -->
</Grid>
</Border>
Rendertransform is a bad idea. Each textbox is in a container.
You should bind the Canvas.Top and Canvas.Left of the itemcontainer. Something like:
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding XviewModelProperty}" />
<Setter Property="Canvas.Top" Value="{Binding YviewModelProperty}" />
</Style>
</ItemsControl.ItemContainerStyle>
Cast the datacontext of your textbox to whatever your viewmodel type is and set XviewModelProperty and YviewModelProperty.
Ensure they raise property changed when set.
Related
I have an Itemsstackpanel inside a Listview Control. I want to fire the PointerWheelChanged event whenever the User is near the edge of a page.
When i put the event on the itemsstackpanel is disables my ability to scroll by mouse wheel. If the event is on the Listview or Grid itself it only works as long as no Items are done loading in the Listview.
Is this intended behaviour or am i missing some important information?
I researched but found no lead to this problem or behaviour.
below is my XAML:
<Grid Background="Gray">
<ProgressRing x:Name="progress" IsActive="False" x:FieldModifier="public" Foreground="Black" Height="200" Width="200"/>
<ListView x:Name="ListViewControl" x:FieldModifier="public" Margin="10,10,10,10" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.ZoomMode="Enabled" ScrollViewer.VerticalScrollBarVisibility="Visible" DataFetchSize="10" IncrementalLoadingTrigger="Edge"
IncrementalLoadingThreshold="2" ShowsScrollingPlaceholders="True" BorderThickness="1" IsItemClickEnabled="False" SelectionMode="None" PointerEntered="ListViewControl_PointerEntered">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel CacheLength="0" Orientation="Vertical" Background="White" PointerWheelChanged="Grid_PointerWheelChanged"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
and the code behind(not done just trying to get it to work for now):
private void ItemsStackPanel_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
e.Handled = true;
PointerPoint pointerPoint = e.GetCurrentPoint(ListViewControl);
float scrolledDistance = pointerPoint.Properties.MouseWheelDelta;
if (scrolledDistance >=120)
{
//Load Page above current Page at a certain mousewheel point
}
else if (scrolledDistance <= -120 )
{
//Load Page below current Page at certain mousewheel point
}
else
{
//do some other stuff
}
}
The default ControlTemplate of ListView contains a ScrollViewer control. PointerWheelChanged is a relatively low-level event that will be intercepted by ScrollViewer.
If you want to monitor the change of the scrolling distance, PointerWheelChanged is not a recommended event. You can listen to the ScrollViewer.ViewChanged event and use ScrollViewer.VerticalOffset to determine the vertical scrolling distance.
We can create a custom ListView to achieve this:
CustomListView.cs
public class CustomListView:ListView
{
private ScrollViewer _scrollViewer;
public event EventHandler<ScrollViewerViewChangedEventArgs> ViewChanged;
public CustomListView()
{
this.DefaultStyleKey = typeof(ListView);
}
protected override void OnApplyTemplate()
{
_scrollViewer = GetTemplateChild("ScrollViewer") as ScrollViewer;
if (_scrollViewer != null)
{
_scrollViewer.ViewChanged += CustomViewChange;
}
base.OnApplyTemplate();
}
private void CustomViewChange(object sender, ScrollViewerViewChangedEventArgs e)
{
ViewChanged?.Invoke(sender, e);
}
}
Usage
<controls:CustomListView x:Name="ListViewControl"
ViewChanged="ListViewControl_ViewChanged"
...>
<!--other content-->
</controls:CustomListView>
private void ListViewControl_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
var scrollViewer = sender as ScrollViewer;
double scrollHeight = scrollViewer.VerticalOffset;
if (scrollHeight > 120)
{
//Do Something...
}
else
{
//Do something...
}
}
I am new to MVVM and I am currently trying to add the drag/drop feature to my application. The thing is I already developed the interface in the code-behind but I am trying now to re-write the code into MVVM as I am only at the beginning of the project.
Here is the context: the user will be able to add boxes (ToggleButton but it may change) to a grid, a bit like a chessboard. Below is the View Model I am working on:
<Page.Resources>
<Style TargetType="{x:Type local:AirportEditionPage}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Page}">
<!-- The page content-->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ToolKitWidth, FallbackValue=50}" />
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="{Binding RightPanelWidth, FallbackValue=400}"/>
</Grid.ColumnDefinitions>
<!-- The airport grid where Steps and Links are displayed -->
<ScrollViewer Grid.ColumnSpan="4" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Viewbox Height="{Binding AirportGridHeight}" Width="{Binding AirportGridWidth}" RenderOptions.BitmapScalingMode="HighQuality">
<ItemsControl x:Name="ChessBoard" ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="{Binding CardQuantityRow}" Height="{Binding CardQuantityColumn}" Background="{StaticResource AirportGridBackground}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="1" Height="1">
<ToggleButton Style="{StaticResource StepCardContentStyle}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Pos.X}"/>
<Setter Property="Canvas.Top" Value="{Binding Pos.Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Viewbox>
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
Items are basically from a class (child of INotifiedPropertyChanged) with a name, an icon and a position (Point).
Now, I am trying to make the user able to drag and drop the box (ToggleButton) within the grid wherever he/she wants. However, I am totally lost with Commands, AttachedProperties etc. I spent all the whole day on tutorials and tried drag/drop solutions but with my poor knowledge, I don't know how to apply all of this into my code.
On my code-behinded version of the code, it was easy. When the button is left-clicked, I say to a variable of the grid "hey, I am being dragged and dropped". While the user is moving, I changed the Item coordinates and when the user released the left button (left button up), the dragdrop_object variable comes null again.
In the frame of the MVVM, I am totally lost. Could you give me some tracks to help me trough ? I intended to give up with MVVM a lot of time, but I know that it is better to keep up even if every little feature takes litteraly hours for me to implement (it should decrease with time...).
Do not hesitate if you need further details to answer to my question.
I found the solution here : Move items in a canvas using MVVM and here : Combining ItemsControl with draggable items - Element.parent always null
To be precise, here is the code I added :
public class DragBehavior
{
public readonly TranslateTransform Transform = new TranslateTransform();
private static DragBehavior _instance = new DragBehavior();
public static DragBehavior Instance
{
get { return _instance; }
set { _instance = value; }
}
public static bool GetDrag(DependencyObject obj)
{
return (bool)obj.GetValue(IsDragProperty);
}
public static void SetDrag(DependencyObject obj, bool value)
{
obj.SetValue(IsDragProperty, value);
}
public static readonly DependencyProperty IsDragProperty =
DependencyProperty.RegisterAttached("Drag",
typeof(bool), typeof(DragBehavior),
new PropertyMetadata(false, OnDragChanged));
private static void OnDragChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// ignoring error checking
var element = (UIElement)sender;
var isDrag = (bool)(e.NewValue);
Instance = new DragBehavior();
((UIElement)sender).RenderTransform = Instance.Transform;
if (isDrag)
{
element.MouseLeftButtonDown += Instance.ElementOnMouseLeftButtonDown;
element.MouseLeftButtonUp += Instance.ElementOnMouseLeftButtonUp;
element.MouseMove += Instance.ElementOnMouseMove;
}
else
{
element.MouseLeftButtonDown -= Instance.ElementOnMouseLeftButtonDown;
element.MouseLeftButtonUp -= Instance.ElementOnMouseLeftButtonUp;
element.MouseMove -= Instance.ElementOnMouseMove;
}
}
private void ElementOnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
((UIElement)sender).CaptureMouse();
}
private void ElementOnMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
((UIElement)sender).ReleaseMouseCapture();
}
private void ElementOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
{
FrameworkElement element = sender as FrameworkElement;
Canvas parent = element.FindAncestor<Canvas>();
var mousePos = mouseEventArgs.GetPosition(parent);
if (!((UIElement)sender).IsMouseCaptured) return;
if (mousePos.X < parent.Width && mousePos.Y < parent.Height && mousePos.X >= 0 && mousePos.Y >=0)
((sender as FrameworkElement).DataContext as Step).Pos = new System.Drawing.Point(Convert.ToInt32(Math.Floor(mousePos.X)), Convert.ToInt32((Math.Floor(mousePos.Y))));
}
}
And my DataTemplate is now:
<DataTemplate>
<ContentControl Height="1" Width="1" local:DragBehavior.Drag="True" Style="{StaticResource StepCardContentControl}"/>
</DataTemplate>
I added the FindAncestor static class in a dedicated file like this:
public static class FindAncestorHelper
{
public static T FindAncestor<T>(this DependencyObject obj)
where T : DependencyObject
{
DependencyObject tmp = VisualTreeHelper.GetParent(obj);
while (tmp != null && !(tmp is T))
{
tmp = VisualTreeHelper.GetParent(tmp);
}
return tmp as T;
}
}
(My items are now ContentControls).
As the items' positions within the canvas are directly managed with their Pos variable (Canvas.SetLeft and Canvas.SetTop based on Pos (Pos.X and Pos.Y) with Binding), I just update it according to the MousePosition within the Canvas.
Also, as suggested in a commentary, I will see if there is something better than the ScrollViewer and Viewbox I'm using.
In UWP I have GridView. That GridView has ItemTemplate like this:
<Page.Resources>
<DataTemplate x:Key="Template" x:DataType="local:ModelClass">
<local:CustomUserControl
Model="{x:Bind Mode=OneWay}"/>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<GridView x:Name="gvMain" ItemTemplate="{StaticResource Template}" SelectionChanged="gvMain_SelectionChanged">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Vertical"
Margin="0,0,0,-10"
MaximumRowsOrColumns="1"
ItemWidth="50"
ItemHeight="50"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</Grid>
The usercontrol is like this:
<Grid x:Name="gridMain" Width="50" Height="50">
<Grid VerticalAlignment="Top" HorizontalAlignment="Right" Margin="0, 0, -10, 0" Width="20" Height="20" Background="Pink"/>
</Grid>
And in codebehind:
public ModelClass Model
{
get { return (ModelClass)GetValue(ModelProperty); }
set { SetValue(ModelProperty, value); SetBackground(); }
}
public static readonly DependencyProperty ModelProperty =
DependencyProperty.Register("Model", typeof(ModelClass), typeof(CustomUserControl), new PropertyMetadata(new ModelClass()));
private void SetBackground()
{
if (Model == null)
{
return;
}
gridMain.Background = Model.BackgroundColor;
}
public CustomUserControl()
{
this.InitializeComponent();
}
I am populating the GridView like this:
List<ModelClass> list = new List<ModelClass>();
ModelClass mc = new ModelClass();
mc.BackgroundColor = new SolidColorBrush(Colors.Red);
ModelClass mc1 = new ModelClass();
mc1.BackgroundColor = new SolidColorBrush(Colors.Blue);
ModelClass mc2 = new ModelClass();
mc2.BackgroundColor = new SolidColorBrush(Colors.Yellow);
ModelClass mc3 = new ModelClass();
mc3.BackgroundColor = new SolidColorBrush(Colors.Green);
list.Add(mc);
list.Add(mc1);
list.Add(mc2);
list.Add(mc3);
gvMain.ItemsSource = list;
And what it looks like is this:
On each item there is a small square in upper right corner, colored in pink.
When I click on some item, I want that item to overlap every other items, so my pink square will be visible.
How to change Z-index of GridView items in this case?
How to change Z-index of GridView items in this case?
If you want to change the GridViewItem's Z-Index, you might think about using canvas relevant panel as the GridView's ItemsPanel.
Then, in your SelectionChanged event handler method, you could use the Canvas.SetZIndex(UIElement, index) method to set current selected item's Canvas.ZIndex. It would get your effect that you want.
But, if you used the general Canvas control as the GridView's ItemsPanel. You would find that the items would not be shown as the general items list.
As a result, in your case, you also would need to define your custom panel. You could need to rearrange the items in it.
I've made a simple for your reference:
<GridView x:Name="gvMain" SelectionChanged="gvMain_SelectionChanged">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<local:CustomPanel></local:CustomPanel>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridViewItem >
<Rectangle Fill="Red" Width="50" Height="50"></Rectangle>
</GridViewItem>
<GridViewItem >
<Rectangle Fill="Blue" Width="50" Height="50" ></Rectangle>
</GridViewItem>
<GridViewItem>
<Rectangle Fill="Yellow" Width="50" Height="50"></Rectangle>
</GridViewItem>
</GridView>
public class CustomPanel:Canvas
{
protected override Size MeasureOverride(Size availableSize)
{
Size s = base.MeasureOverride(availableSize);
foreach (UIElement element in this.Children)
{
element.Measure(availableSize);
}
return s;
}
protected override Size ArrangeOverride(Size finalSize)
{
this.Clip = new RectangleGeometry { Rect = new Rect(0, 0, finalSize.Width, finalSize.Height) };
Double position = 0d;
foreach (UIElement item in this.Children)
{
if (item == null)
continue;
Size desiredSize = item.DesiredSize;
if (double.IsNaN(desiredSize.Width) || double.IsNaN(desiredSize.Height)) continue;
var rect = new Rect(0, position, desiredSize.Width, desiredSize.Height);
item.Arrange(rect);
TranslateTransform compositeTransform = new TranslateTransform();
compositeTransform.X = position / 2;
item.RenderTransform = compositeTransform;
position += desiredSize.Width;
}
return finalSize;
}
}
private void gvMain_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
GridViewItem item = (sender as GridView).SelectedItem as GridViewItem;
if (item != null)
{
Canvas.SetZIndex(item,index);
}
}
This just is a simple code sample, you could do more custom behavior in your custom panel.
The following are the effect screenshots:
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 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);
}