Hi
I am trying to convert an OrgChart program I wrote for Silverlight v12.1 to WPF4 v15.2.
Just about everything works fine - the only issue I have is that in dragging a chart shape from one node to another there is no visible drag taking place. The drag however does work and the shape does move from one node to another - its just that the user has no idea what is being dragged.
I have looked through the samples and I am not using any drag template (this was not needed in SL v12.1) so this may be the issue. If this is the case do you have an example of creating a drag template at runtime in code (not xaml)?
If it helps my code is shown below
With thanks
Andrew Hunot
Private OrganizationModel As New OrganizationViewModel()
Public Sub New()
' This call is required by the designer. InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
Public Function CreateChart(ByVal sXML As String) As Boolean
Me.OrganizationModel.Employees = New ObservableCollection(Of Employee)()
Try
Dim xd As XDocument xd = XDocument.Parse(sXML)
For Each xe As XElement In xd.Nodes If xe.Name = "EmployeeData" Then For Each xf As XElement In xe.Nodes If xf.Name = "Employees" Then Dim NewEmployee As New Employee CreateNodes(xf.Nodes, NewEmployee) Me.OrganizationModel.Employees.Add(NewEmployee) End If Next End If Next
Catch ex As Exception
Debug.Print(ex.Message)
Return False
End Try
Me.OrgChart.DataContext = Me.OrganizationModel
Return True
End Function
Public Sub SetScaleToFit()
Me.OrgChart.ScaleToFit()
Private Sub CreateNodes(ByVal nodes As Object, ByRef ThisEmployee As Employee)
ThisEmployee.Subordinates = New ObservableCollection(Of Employee)
For Each xe As XElement In nodes
If xe.Name = "EmpID" Then ThisEmployee.Id = xe.Value End If If xe.Name = "EmpName" Then ThisEmployee.Name = xe.Value End If If xe.Name = "EmpAttr" Then ThisEmployee.Attributes = xe.Value End If
If xe.Name = "Employees" Then Dim NewEmployee As New Employee CreateNodes(xe.Nodes, NewEmployee) ThisEmployee.Subordinates.Add(NewEmployee) End If
Next
Private Sub OrgChart_NodeControlAttachedEvent(sender As Object, e As OrgChartNodeEventArgs)
Dim employee As Employee = DirectCast(e.Node.Node.Data, Employee) If employee.Attributes <> "" Then e.Node.Background = New SolidColorBrush(ColorConverter.FromString(employee.Attributes))
'Create a new DragSource object. Dim dragSource As New DragSource() dragSource.IsDraggable = True 'dragSource.DragChannels = assign drag channels AddHandler dragSource.Drop, AddressOf Node_Drop
'Make the Node a Drag Source. DragDropManager.SetDragSource(e.Node, dragSource)
'Create a new DropTarget object. Dim dropTarget As New DropTarget() dropTarget.IsDropTarget = True 'dropTarget.DropChannels = assign drop channels
'Make the Node a Drop Target. DragDropManager.SetDropTarget(e.Node, dropTarget)
Private Sub Node_Drop(sender As Object, e As DropEventArgs)
Dim DropOnNode As Boolean = False
'Get the dragged OrgChartNodeControl object. Dim draggedNodeControl As OrgChartNodeControl = TryCast(e.DragSource, OrgChartNodeControl) 'Get the dragged OrgChartNode object. Dim draggedNode As OrgChartNode = draggedNodeControl.Node 'Get the underlying data. Dim draggedData As Object = draggedNode.Data Dim draggedEmployee As Employee = Nothing FindEmployee(draggedData.Id, Me.OrganizationModel.Employees, draggedEmployee)
If IsNothing(draggedEmployee) Then Exit Sub
'Get the dropped OrgChartNodeControl object. Dim droppedEmployee As Employee = Nothing Dim droppedNodeControl As OrgChartNodeControl = TryCast(e.DropTarget, OrgChartNodeControl) If Not IsNothing(droppedNodeControl) Then DropOnNode = True 'Get the dropped OrgChartNode object. Dim droppedNode As OrgChartNode = droppedNodeControl.Node 'Get the underlying data. Dim droppedData As Object = droppedNode.Data FindEmployee(droppedData.Id, Me.OrganizationModel.Employees, droppedEmployee) End If
If IsNothing(droppedEmployee) Then Exit Sub
'Prevent drop on self. If draggedEmployee.Id = droppedEmployee.Id Then Exit Sub
'Check if the drop target already contains the dragged node. If droppedEmployee.Subordinates.Contains(draggedEmployee) Then Exit Sub
'Remove the dragged Employee from old node If Me.OrganizationModel.Employees.Contains(draggedEmployee) Then Me.OrganizationModel.Employees.Remove(draggedEmployee) Else RemoveEmployee(draggedEmployee, Me.OrganizationModel.Employees) End If
'Add the dragged employee to new node If DropOnNode Then If Not IsNothing(droppedEmployee) Then droppedEmployee.Subordinates.Add(draggedEmployee) End If Else Me.OrganizationModel.Employees.Add(draggedEmployee) End If
Private Sub FindEmployee(ByVal Id As String, ByRef Employees As ObservableCollection(Of Employee), ByRef FoundEmployee As Employee)
If IsNothing(FoundEmployee) Then For Each emp As Employee In Employees If IsNothing(FoundEmployee) Then If emp.Id = Id Then FoundEmployee = emp Else FindEmployee(Id, emp.Subordinates, FoundEmployee) End If End If Next End If
Private Sub RemoveEmployee(ByVal Employee As Employee, ByRef Employees As ObservableCollection(Of Employee))
If Not IsNothing(Employee) Then For Each emp As Employee In Employees If emp.Subordinates.Count > 0 Then If emp.Subordinates.Contains(Employee) Then emp.Subordinates.Remove(Employee) Else RemoveEmployee(Employee, emp.Subordinates) End If End If Next End If
Hi,
A drag template property is of type DataTemplate. So I think it is possible to create a datatemplate in code behind and to apply it on drag start as it is shown in our samples.
Thanks,
M.Yovchev
I have looked at your samples which don't seem to include the creation of a DataTemplate in code.
I have however created a new DataTemplate at runtime with the following code:
nodeDragTemplate = New DataTemplate(GetType(Employee))
I do now get a drag object in the form of a very small hand which is better than nothing but still doesn't show the user that an Employee is being dragged.
I can't seem to set any other properties of the nodeDragTemplate at runtime so would be grateful if you could help me out or point me to some samples that do what I require.
I have attached the code files if these are of help to you.
Andfrew
Sorry if I was not clear enough. I meant that in our samples is shown where to apply the template if you have it.
From the files you have provided I saw that you are creating a DataTemplate but not assigning its visual tree any elements which may explain why you dons see any template. One approach to create it in code is using xamreader and provide a string that is the same as in XAML file. Here is a very simple example:
Private Function Create() As DataTemplate Dim str = "<DataTemplate" + "xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">" + "<TextBlock Text=""text""/>" + "</DataTemplate>"
Dim stringReader As New StringReader(str) Dim xmlReader__1 As XmlReader = XmlReader.Create(stringReader) Return TryCast(XamlReader.Load(xmlReader__1), DataTemplate) End Function
You can try it like assign it to your nodeDragTemplate. If it works you can try to modify it so that the template has the representation of the node.
Many thanks for the speedy reply - this has been most helpful. I have created a function similar to the one you suggested:
Private Function CreateTemplate(ByVal nText As String) As DataTemplate
Dim str = "<DataTemplate " + "xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">" + "<Border BorderThickness=""3"" BorderBrush=""#FF617584"" Background=""#FFC9CACA"" Opacity="".5"">" + "<TextBlock Margin=""3"" Text=""" + nText + """/>" + "</Border>" + "</DataTemplate>"
Dim stringReader As New StringReader(str) Dim xmlReader__1 As XmlReader = XmlReader.Create(stringReader) Return TryCast(XamlReader.Load(xmlReader__1), DataTemplate)
I then call it in the NodeControlAttachedEvent event as follows:
...dragSource.DragTemplate = CreateTemplate(employee.Name.Replace(vbLf, Environment.NewLine))
and it works really well.
I only have one issue in that the TextBlock does not seem to recognise the Environment.NewLine character and shows everything on one line. I can live with this but if you had any ideas on how to show multiple lines that would be great.
Again many thanks for your help.
Andrew
Yes it does not recognize it. You may giva to the function not one string, but an array of strings to you function and use XAML like this:
<TextBlock> line1 <LineBreak/> line2</TextBlock>
or use binding
<TextBlock Text="{Binding MyText}"/>
and set MyText like this:
Public ReadOnly Property MyText() As String Get Return String.Format("My Text " & Environment.NewLine & " Your Text") End Get End Property
Hope this will be helpful.
I couldn't get the Binding approach to work but manipulating the XAML with Line Breaks works really well - all I needed to was one extra line in the CreateTemplate function as follows:
nText = nText.Replace(vbLf, "<LineBreak/>")
Many thanks again for your help in this.
Regards