I have a Universal Windows Application for a local bank, I'm working on a money transfer view, and they need to transfer money from account to account using the Drag and Drop feature in UWP applications.
I've made the animation part, but I need help after I drop the list item to the "Account To" list.
I'll attach a screenshot to make it clear.
As you see in the picture, I need to drag one item from the "From Account" list and drop it on only one item on "To Account" list. How can I achieve this ?
I've created a small sample which shows drag-drop between two ListViews filled with some Accounts. I will skip the implementation of UserControls - the Page xaml looks like this:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="200"/>
<RowDefinition Height="200"/>
</Grid.RowDefinitions>
<ListView Header="Source" Margin="10" Grid.Row="0" CanDragItems="True" ItemsSource="{x:Bind Accounts}" SelectionMode="None">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<controls:AccountControl CanDrag="True" DragStarting="AccountControl_DragStarting"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView Header="Targets" Margin="10" Grid.Row="1" ItemsSource="{x:Bind Accounts}" SelectionMode="None">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<controls:AccountControl AllowDrop="True" DragEnter="AccountControl_DragEnter" Drop="AccountControl_Drop"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
As you can see there is a Source list in which the control is firing an event when it's being dragged.
private void AccountControl_DragStarting(UIElement sender, DragStartingEventArgs args)
{
if ((sender as AccountControl)?.DataContext is Account account)
{
args.AllowedOperations = DataPackageOperation.Link;
args.Data.SetData(accountId, account.Id);
}
}
The Account class apart from name and balance has a Guid identifier so I can use it to pass information which source account has been used in transfer method.
The items in second list (Targets) accepts only drop operation and for this purpose fire two events:
private void AccountControl_DragEnter(object sender, DragEventArgs e)
{
e.AcceptedOperation = DataPackageOperation.Link;
e.DragUIOverride.Caption = "Transfer";
}
private async void AccountControl_Drop(object sender, DragEventArgs e)
{
if ((e.OriginalSource as AccountControl)?.DataContext is Account targetAccount)
if (await (e.DataView.GetDataAsync(accountId)) is Guid sourceAccountId)
{
var sourceAccount = Accounts.First(x => x.Id == sourceAccountId);
sourceAccount.Balance -= 1000;
targetAccount.Balance += 1000;
}
}
The first one sets accepted operation and some information for the user. The second one 'transfers' some money from one account to the second.
Everything looks like this:
Some more help you can find at MS directly, other article and in MS samples repository.
I am not fully satisfied with the "solutions" which I will provide. They are much likely very far away from the ideal implementations, but ...
The XAML code which I created to try to replicate as easily, but also consistently your object, consisted in a group of draggable Rectangles inside a StackPanel Control, plus another StackPanel Control where the items could be dragged into.
<Grid>
<Grid.Resources>
<Style TargetType="Rectangle">
<Setter Property="Width" Value="300"/>
<Setter Property="Height" Value="300"/>
<Setter Property="CanDrag" Value="True"/>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Name="StackPanelRectangles" Grid.Row="0" Orientation="Horizontal">
<Rectangle x:Name="RedRect" Fill="Red" DragStarting="Rectangle_DragStarting"/>
<Rectangle x:Name="GreenRect" Fill="Green" DragStarting="Rectangle_DragStarting"/>
<Rectangle x:Name="BlueRect" Fill="Blue" DragStarting="Rectangle_DragStarting"/>
</StackPanel>
<StackPanel Name="StackPanelDropArea" Background="Azure" AllowDrop="True"
DragOver="StackPanel_DragOver" Drop="StackPanel_Drop"
Grid.Row="2" Orientation="Horizontal"
HorizontalAlignment="Center">
<TextBlock>Drop anywhere in this area area</TextBlock>
</StackPanel>
</Grid>
1st Solution:
I routed every DragStarting event of the multiple Rectangles to the same EventHandler. In this EventHandler, we have access to the UIElement which is being dragged, so with an exposed property of type UIElement in your Page class, and you can simply clone the necessary properties for when you need to drop it, like this:
UIElement dragItem;
private void Rectangle_DragStarting(UIElement sender, DragStartingEventArgs args)
{
dataPackage.RequestedOperation = DataPackageOperation.Copy;
dragItem = sender;
}
Then when the item is dropped EventHandler is called, I have simply add it onto my DropArea.
private void StackPanel_Drop(object sender, DragEventArgs e)
{
Rectangle newElement = new Rectangle();
newElement.Width = (dragItem as Rectangle).Width;
newElement.Height = (dragItem as Rectangle).Height;
newElement.Fill = (dragItem as Rectangle).Fill;
StackPanelDropArea.Children.Add(newElement);
}
You cannot add your new Control by setting to reference the object being dragged, since there are properties such as the respective Parent which will thrown an exception when you try to add the Control to a different container.
2nd Solution:
I was extremely focused on on utilizing the DataPackage object, and one of its supported default formats, but I don't think any of them can actually hold data of an Object, such as our UIElement.
But each DataPackage instance supports a set of properties, which corresponds a Dictionary. We can set the Dictionary to hold UIElement in there, as long as we specify a key to reference that same object later on.
private void Rectangle_DragStarting(UIElement sender, DragStartingEventArgs args)
{
dataPackage.RequestedOperation = DataPackageOperation.Copy;
args.Data.Properties.Add("myRectangle", sender);
}
In the drop Event Handler, you can obtain the UIElement, like such:
private async void StackPanel_Drop(object sender, DragEventArgs e)
{
Rectangle element = e.DataView.Properties["myRectangle"] as Rectangle;
......
......
}
3rd Solution:
This solution used the method SetText(String) exposed by DataPackage, to hold the value of the Name property of the UIElement being dragged.
private void Rectangle_DragStarting(UIElement sender, DragStartingEventArgs args)
{
dataPackage = new DataPackage();
dataPackage.RequestedOperation = DataPackageOperation.Copy;
Rectangle rectangle = sender as Rectangle;
dataPackage.SetText(rectangle.Name);
Clipboard.SetContent(dataPackage);
}
By knowing the value of the Name property of the UIElement which is being dragged, looked for it, by using the VisualTreeHelper Class, like this:
private async void StackPanel_Drop(object sender, DragEventArgs e)
{
DataPackageView dataPackageView = Clipboard.GetContent();
if (dataPackageView.Contains(StandardDataFormats.Text))
{
draggedObject = await dataPackageView.GetTextAsync();
}
// Dragged objects come from another one of our Parent's Children
DependencyObject parent = VisualTreeHelper.GetParent(StackPanelDropArea);
int count = VisualTreeHelper.GetChildrenCount(parent);
for(int i=0; i< count; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if(child.GetType().Equals(typeof(StackPanel)))
{
StackPanel currentStackPanel = child as StackPanel;
if(currentStackPanel.Name == "StackPanelRectangles")
{
int numberOfRectangles = VisualTreeHelper.GetChildrenCount(currentStackPanel);
for(int j=0; j<numberOfRectangles; j++)
{
if(VisualTreeHelper.GetChild(currentStackPanel,j).GetType().Equals(typeof(Rectangle)))
{
Rectangle currentRectangle = VisualTreeHelper.GetChild(currentStackPanel, j) as Rectangle;
if (draggedObject != string.Empty && currentRectangle.Name.Equals(draggedObject))
{
Rectangle newRectangle = new Rectangle();
newRectangle.Width = currentRectangle.Width;
newRectangle.Height = currentRectangle.Height;
newRectangle.Fill = currentRectangle.Fill;
StackPanelDropArea.Children.Add(newRectangle);
}
}
}
}
}
} */
}
Result:
I usually try tackling this several times before giving up and using a third party library. The one I typically use is:
https://github.com/punker76/gong-wpf-dragdrop
You may subscribe to PointerPressed event in your DataTemplate and extract all the things you need.
XAML:
<DataTemplate x:Name="DataTemplate">
<Grid Background="Transparent" PointerPressed="Grid_OnPointerPressed"/>
</DataTemplate>
Code:
private void Grid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
//your FrameworkElement
var frameworkElement = sender as FrameworkElement;
//global position of your element
var itemPosition = frameworkElement.TransformToVisual(Window.Current.Content).TransformPoint(new Point(0, 0)).ToVector2();
//your data
var selectedItemData = frameworkElement.DataContext as ItemData;
}
Save your data, use UWP Drag'n'Drop. On drop load your data.
Related
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.
I know how to set properties like CanDrag and AllowDrop and define DragOver method and Drop method.
I just don't know what to write inside the Drop method.
How to move text from one Textblock to another Textblock using Drag and Drop
We can define DragStarting event for the source Textblock and save the text of the source Textblock in DragStartingEventArgs for transfer during dragging. And accept the text when drop at target Textblock. Read the text from DragEventHandler and set it to the target Textblock.
I wrote a simple sample here, move the text from txtsource to append to txttarget.
XAML code:
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Padding="30">
<Border BorderBrush="Azure" BorderThickness="2">
<TextBlock x:Name="txtsource" Text="I'm the first textblock" CanDrag="True" DragStarting="txtsource_DragStarting" />
</Border>
<Border BorderBrush="Azure" BorderThickness="2" Margin="20" AllowDrop="True" >
<TextBlock x:Name="txttarget" Text="I'm the second textblock" Drop="txttarget_Drop" Height="50" Width="400" AllowDrop="True" DragEnter="txttarget_DragEnter"/>
</Border>
</StackPanel>
Code behind
private void txtsource_DragStarting(UIElement sender, DragStartingEventArgs args)
{
args.Data.SetText(txtsource.Text);
}
private async void txttarget_Drop(object sender, DragEventArgs e)
{
bool hasText = e.DataView.Contains(StandardDataFormats.Text);
e.AcceptedOperation = hasText ? DataPackageOperation.Copy : DataPackageOperation.None;
if (hasText)
{
var text = await e.DataView.GetTextAsync();
txttarget.Text +="\n"+ text;
}
}
private void txttarget_DragEnter(object sender, DragEventArgs e)
{
bool hasText = e.DataView.Contains(StandardDataFormats.Text);
e.AcceptedOperation = hasText ? DataPackageOperation.Copy : DataPackageOperation.None;
if (hasText)
{
e.DragUIOverride.Caption = "Drop here to insert text";
}
}
I use DragOver event to help define which area can be drop. More details please reference the scenario 2 of the official sample.
I am using a ListView element in my XAML:
<ListView
x:Name="myList"
IsItemClickEnabled="true"
ItemClick="onDrawerItemClick"
SelectionMode="Single"
ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ListView.ItemTemplate>
<DataTemplate>
<Grid
Width="260">
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="44" />
<ColumnDefinition
Width="*" />
</Grid.ColumnDefinitions>
<Image
x:Name="image"
Source="{Binding myIcon}"
Grid.Column="0" />
<TextBlock
Text="{Binding myTxt}"
Grid.Column="1" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
and I populate it using Bind property like so:
List<MyObj> listData = a list with title + image uri;
myList.ItemsSource = listData;
I need to disable click only for some items depending on some value from MyObj in my list but the others to have it. In Android we use adapter for that, how should I handle it here?
First, you should create a new bool property called Disabled inside your MyObj object.
Then, subscribe to myList's ContainerContentChanging event where you have access to the ListViewItem and its corresponding Item, which in this case is your MyObj. So, if MyObj.Disabled is true, make that ListViewItem non-clickable.
private void myList_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
var listViewItem = args.ItemContainer;
if (listViewItem != null)
{
var model = (MyObj)args.Item;
if (model.Disabled)
{
listViewItem.IsHitTestVisible = false;
// OR
//listViewItem.IsEnabled = false;
}
}
}
Keep in mind that you might want to use listViewItem.IsEnabled = false if you want that item to appear dimmed. This is because the default ListViewItemstyle has a Disabled state that reduces its Opacity; while setting listViewItem.IsHitTestVisible = false won't change its appearance in any way.
The listView is a strange control as it does not have any mechanism to disable selection.
So what I suggest you do is rather handle the event that notifies the framework that an item has been selected by attaching an event handler to ItemSelectionChanged and in there perform a deselect on the item:
yourListView.ItemSelectionChanged += yourListView_ItemSelectionChanged;
private void yourListView_ItemSelectionChanged(
object sender,
ListViewItemSelectionChangedEventArgs e)
{
if (e.IsSelected)
e.Item.Selected = false;
}
Please let me know if my answer helps :)
I am working on windows phone 8 app.
I have List box with over 200 items to display.
<DataTemplate x:Key="DataTemplate1">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="White" Height="400" Width="400" CornerRadius="30,30,30,30">
</Border>
<Grid Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Top">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="5,20,5,5"
Foreground="#000000"
Text="{Binding Title}"/>
</Grid>
</Grid>
</DataTemplate>
But it crashes, i have debugged it till 100 items it works but after that it crashes.
In the PhoneApplicationPage_Loaded method i have
private void PhoneApplicationPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
myList.Add(new MyObject("A","A value"));
myList.Add(new MyObject("B", "B value"));
myList.Add(new MyObject("C", "C value"));
and so on... 200 items
ListBoxItems.ItemsSource = myList;
}
how can i fix this ?
Update :
<ItemsPanelTemplate x:Key="ItemsPanelTemplate">
<local:CollectionFlowPanel ItemHeight="400"
ItemWidth="400"
FocusedItemOffset="120"
UnfocusedItemOffset="20"
ItemVisibility="5">
<VirtualizingStackPanel />
</local:CollectionFlowPanel>
</ItemsPanelTemplate>
</phone:PhoneApplicationPage.Resources>
<Grid x:Name="LayoutRoot" Background="#000000">
<local:CollectionFlow x:Name="ListBoxItems"
ItemTemplate="{StaticResource DataTemplate}"
ItemsPanel="{StaticResource ItemsPanelTemplate}"/>
</Grid>
Ensure you have VirtualizingStackPanel inside the ItemsPanelTemplate of your list box, see this answer for more info.
Here's the XAML you likely need for your ListBox:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
You need to read following blog from msdn on visualization of the data in list and grid.
Using virtualization with a list or grid
Without seeing your whole xaml code I cannot suggest the exact answer but my guess is that you in xaml ListBox is placed inside a canvas/StackPanel or scrollviewer control.
When the size of the ItemsControl's viewport isn't restricted, the control doesn't perform virtualization. Instead, it creates an item container for each item in its collection. Some common containers that don't restrict the viewport size are Canvas, StackPanel, and ScrollViewer. You can enable virtualization in this situation by setting the size of ItemsControl directly, instead of letting it be sized by its parent container.
Here, we set the Height and Width on the GridView. This restricts the size of the viewport, and items outside of the viewport are virtualized.
Below are 2 scenarios one will throw out of memory exception and other will work fine(use your same code behind and test)
1. ListBox in Canvas
<Canvas .....
<ListBox Name="ListBoxItems".....
</ListBox>
</Canvas>
Above code will throw out of memory exception as items control's viewport is not defined (if you still want to use Canvas than define width/height if ListBox in that case the port of Items control is defined and virtulazation will apply)
2. ListBox in Grid
<Grid .....
<ListBox Name="ListBoxItems".....
</ListBox>
</Grid>
The above code will not throw out of memory exception as virtuallization is applied on the listbox.
Hope this will help
How big is your object ? If your object is too big you might not be able to load them all at once.
Did you try using the for loop?
public List<Fellow> fellowList { get; set; }
private void PhoneApplicationPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
fellowList = new List<Fellow>();
for (int i = 0; i < 2; i++)
{
Fellow fellow = new Fellow();
fellow.x = "B" + i;
fellow.value = "B Value" + i;
fellowList.Add(fellow);
}
this.DataContext = this;
ListBoxItems.ItemsSource = fellowList;
}
public class Fellow
{
public string x { get; set; }
public string value { get; set; }
}
Hope it helps..change the view model according to your wish
I'm trying to create a tree header within a TreeView, but nothing will display. In my program, I'm creating a series of ellipse objects on a canvas that have names. What I'm trying to do is this:
When the ellipse object is created, create a header within the TreeView using the ellipse' names.
Since the first ellipse has a name of "Circle01," this is what I'm trying to display in the TreeView when the Left Mouse button is released. But when I do so, nothing happens.
// Ellipse object is created when Left Mouse button is release.
private void _canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (mouseLBDown)
{
mouseLBDown = false;
if (isCreatingEllipse)
{
_debugLabel.Content = "Done.";
isCreatingEllipse = false;
loadToTree = true;
}
}
}
// Suppose to load name of Ellipse as a header, but doesn't.
private void _circleTree_Loaded(object sender, RoutedEventArgs e)
{
if (loadToTree)
{
TreeViewItem item = new TreeViewItem();
item.Header = circle[0].circleName;
_circleTree.Items.Add(item);
loadToTree = false;
}
}
XAML:
<TreeView Name="_boneTree" Background="#404040" Foreground="#E0E0E0"
HorizontalAlignment="Left" Height="422" Margin="970,28,0,0"
VerticalAlignment="Top" Width="212" Loaded="_circleTree_Loaded"/>
You should not create your own TreeViewItems, rather set the TreeView's ItemsSource and let WPF build the TreeViewItems for you. Also it is easier to do this in XAML:
<TreeView Name="_boneTree" Background="#404040" Foreground="#E0E0E0"
HorizontalAlignment="Left" Height="422" Margin="970,28,0,0"
VerticalAlignment="Top" Width="212"
ItemsSource="{Binding Circles}">
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="Header" Value="{Binding circleName}"/>
</Style>
</TreeView.Resources>
</TreeView>
Where Circles is a property on your DataContext which by default will be your codebehind:
public ObservableCollection<Circle> Circles { get; set; }
Then whenever a Circle is added or removed it will automatically show up on the UI!