diff --git a/AIStudio.Wpf.DiagramApp/ViewModels/PageViewModel.cs b/AIStudio.Wpf.DiagramApp/ViewModels/PageViewModel.cs index e8ca2dc..738179b 100644 --- a/AIStudio.Wpf.DiagramApp/ViewModels/PageViewModel.cs +++ b/AIStudio.Wpf.DiagramApp/ViewModels/PageViewModel.cs @@ -391,36 +391,7 @@ namespace AIStudio.Wpf.DiagramApp.ViewModels List viewModels = new List(); foreach (var diagramItem in diagramDocument.DiagramItems) { - var viewModel = GetDiagramViewModel(diagramItem.Name, diagramItem.DiagramType, false); - viewModel.Init(diagramItem); - - foreach (var diagramItemData in diagramItem.DesignerItems) - { - Type type = TypeHelper.GetType(diagramItemData.ModelTypeName); - DesignerItemViewModelBase itemBase = Activator.CreateInstance(type, viewModel, diagramItemData, ext) as DesignerItemViewModelBase; - viewModel.Items.Add(itemBase); - } - - foreach (var connection in diagramItem.Connections) - { - Type type = TypeHelper.GetType(connection.SerializableTypeName); - var connectionItem = SerializeHelper.DeserializeObject(type, connection.SerializableString, ext) as ConnectionItem; - - connectionItem.SourceType = System.Type.GetType(connectionItem.SourceTypeName); - connectionItem.SinkType = System.Type.GetType(connectionItem.SinkTypeName); - DesignerItemViewModelBase sourceItem = DiagramViewModelHelper.GetConnectorDataItem(viewModel.Items, connectionItem.SourceId, connectionItem.SourceType); - ConnectorOrientation sourceConnectorOrientation = connectionItem.SourceOrientation; - FullyCreatedConnectorInfo sourceConnectorInfo = sourceItem.GetFullConnectorInfo(connectionItem.Id, sourceConnectorOrientation, connectionItem.SourceXRatio, connectionItem.SourceYRatio, connectionItem.SourceInnerPoint, connectionItem.SourceInnerPoint); - - DesignerItemViewModelBase sinkItem = DiagramViewModelHelper.GetConnectorDataItem(viewModel.Items, connectionItem.SinkId, connectionItem.SinkType); - ConnectorOrientation sinkConnectorOrientation = connectionItem.SinkOrientation; - FullyCreatedConnectorInfo sinkConnectorInfo = sinkItem.GetFullConnectorInfo(connectionItem.Id, sinkConnectorOrientation, connectionItem.SinkXRatio, connectionItem.SinkYRatio, connectionItem.SinkInnerPoint, connectionItem.SinkInnerPoint); - - ConnectionViewModel connectionVM = new ConnectionViewModel(viewModel, sourceConnectorInfo, sinkConnectorInfo, connectionItem); - connectionVM.Id = Guid.NewGuid(); - viewModel.Items.Add(connectionVM); - } - + var viewModel = GetDiagramViewModel(diagramItem, ext); viewModels.Add(viewModel); } DiagramViewModels = new ObservableCollection(viewModels); @@ -498,6 +469,11 @@ namespace AIStudio.Wpf.DiagramApp.ViewModels InitDiagramViewModel(); } + protected virtual DiagramViewModel GetDiagramViewModel(DiagramItem diagramItem, string ext) + { + return new BlockDiagramViewModel(diagramItem, ext); + } + protected virtual DiagramViewModel GetDiagramViewModel(string name, DiagramType diagramType, bool initNew) { return new BlockDiagramViewModel() { Name = name ?? NewNameHelper.GetNewName(DiagramViewModels.Select(p => p.Name), "页-"), DiagramType = diagramType }; @@ -513,37 +489,11 @@ namespace AIStudio.Wpf.DiagramApp.ViewModels var selectedDesignerItems = viewModel.Items.OfType(); var selectedConnections = viewModel.Items.OfType(); - diagramItem.DesignerItems = selectedDesignerItems.Select(p => p.ToSerializableItem("json")).Where(p => p != null).ToList(); - diagramItem.Connections = selectedConnections.Select(p => p.ToSerializableItem("json")).Where(p => p != null).ToList(); + diagramItem.DesignerItems = selectedDesignerItems.Select(p => p.ToSerializableItem(".json")).Where(p => p != null).ToList(); + diagramItem.Connections = selectedConnections.Select(p => p.ToSerializableItem(".json")).Where(p => p != null).ToList(); + diagramItem.Name = NewNameHelper.GetNewName(DiagramViewModels.Select(p => p.Name), "页-"); - viewModel = GetDiagramViewModel(NewNameHelper.GetNewName(DiagramViewModels.Select(p => p.Name), "页-"), diagramItem.DiagramType, false); - viewModel.Init(diagramItem); - - foreach (var diagramItemData in diagramItem.DesignerItems) - { - Type type = TypeHelper.GetType(diagramItemData.ModelTypeName); - DesignerItemViewModelBase itemBase = Activator.CreateInstance(type, viewModel, diagramItemData, "json") as DesignerItemViewModelBase; - viewModel.Items.Add(itemBase); - } - - foreach (var connection in diagramItem.Connections) - { - var connectionItem = JsonConvert.DeserializeObject(connection.SerializableString); - - connectionItem.SourceType = System.Type.GetType(connectionItem.SourceTypeName); - connectionItem.SinkType = System.Type.GetType(connectionItem.SinkTypeName); - DesignerItemViewModelBase sourceItem = DiagramViewModelHelper.GetConnectorDataItem(viewModel.Items, connectionItem.SourceId, connectionItem.SourceType); - ConnectorOrientation sourceConnectorOrientation = connectionItem.SourceOrientation; - FullyCreatedConnectorInfo sourceConnectorInfo = sourceItem.GetFullConnectorInfo(connectionItem.Id, sourceConnectorOrientation, connectionItem.SourceXRatio, connectionItem.SourceYRatio, connectionItem.SourceInnerPoint, connectionItem.SourceIsPortless); - - DesignerItemViewModelBase sinkItem = DiagramViewModelHelper.GetConnectorDataItem(viewModel.Items, connectionItem.SinkId, connectionItem.SinkType); - ConnectorOrientation sinkConnectorOrientation = connectionItem.SinkOrientation; - FullyCreatedConnectorInfo sinkConnectorInfo = sinkItem.GetFullConnectorInfo(connectionItem.Id, sinkConnectorOrientation, connectionItem.SinkXRatio, connectionItem.SinkYRatio, connectionItem.SinkInnerPoint, connectionItem.SinkIsPortless); - - ConnectionViewModel connectionVM = new ConnectionViewModel(viewModel, sourceConnectorInfo, sinkConnectorInfo, connectionItem); - connectionVM.Id = Guid.NewGuid(); - viewModel.Items.Add(connectionVM); - } + viewModel = GetDiagramViewModel(diagramItem, ".json"); DiagramViewModels.Add(viewModel); DiagramViewModel = viewModel; diff --git a/AIStudio.Wpf.DiagramApp/Views/MainWindow.xaml b/AIStudio.Wpf.DiagramApp/Views/MainWindow.xaml index 19936cc..ed9f781 100644 --- a/AIStudio.Wpf.DiagramApp/Views/MainWindow.xaml +++ b/AIStudio.Wpf.DiagramApp/Views/MainWindow.xaml @@ -2660,14 +2660,14 @@ - - + - - + + diff --git a/AIStudio.Wpf.DiagramDesigner/Controls/DiagramTabControl.cs b/AIStudio.Wpf.DiagramDesigner/Controls/DiagramTabControl.cs new file mode 100644 index 0000000..ecf9af3 --- /dev/null +++ b/AIStudio.Wpf.DiagramDesigner/Controls/DiagramTabControl.cs @@ -0,0 +1,318 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Markup; + +namespace AIStudio.Wpf.DiagramDesigner.Controls +{ + //public class DiagramTabControl : TabControl + //{ + // public DiagramTabControl() + // { + // // Attach event handler to TabControl.SelectionChanged event + // this.SelectionChanged += DiagramTabControl_SelectionChanged; + // this.IsVisibleChanged += DiagramTabControl_IsVisibleChanged; + // } + + // private void DiagramTabControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) + // { + // foreach (var item in this.Items) + // { + // if (item is DiagramViewModel viewModel) + // { + // viewModel.ShowSearch = false; + // } + // } + // } + + // private void DiagramTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e) + // { + // // Iterate through all TabItems and manually set the Visibility property + // foreach (var item in this.Items) + // { + // if (item is DiagramViewModel viewModel) + // { + // if (item != this.SelectedItem) + // { + // viewModel.ShowSearch = false; + // } + // } + // } + // } + //} + + /// + /// Attached properties for persistent tab control + /// + /// By default WPF TabControl bound to an ItemsSource destroys visual state of invisible tabs. + /// Set ikriv:DiagramTabControl.IsCached="True" to preserve visual state of each tab. + /// + public static class DiagramTabControl + { + public static bool GetIsCached(DependencyObject obj) + { + return (bool)obj.GetValue(IsCachedProperty); + } + + public static void SetIsCached(DependencyObject obj, bool value) + { + obj.SetValue(IsCachedProperty, value); + } + + /// + /// Controls whether tab content is cached or not + /// + /// When DiagramTabControl.IsCached is true, visual state of each tab is preserved (cached), even when the tab is hidden + public static readonly DependencyProperty IsCachedProperty = + DependencyProperty.RegisterAttached("IsCached", typeof(bool), typeof(DiagramTabControl), new UIPropertyMetadata(false, OnIsCachedChanged)); + + + public static DataTemplate GetTemplate(DependencyObject obj) + { + return (DataTemplate)obj.GetValue(TemplateProperty); + } + + public static void SetTemplate(DependencyObject obj, DataTemplate value) + { + obj.SetValue(TemplateProperty, value); + } + + /// + /// Used instead of TabControl.ContentTemplate for cached tabs + /// + public static readonly DependencyProperty TemplateProperty = + DependencyProperty.RegisterAttached("Template", typeof(DataTemplate), typeof(DiagramTabControl), new UIPropertyMetadata(null)); + + + public static DataTemplateSelector GetTemplateSelector(DependencyObject obj) + { + return (DataTemplateSelector)obj.GetValue(TemplateSelectorProperty); + } + + public static void SetTemplateSelector(DependencyObject obj, DataTemplateSelector value) + { + obj.SetValue(TemplateSelectorProperty, value); + } + + /// + /// Used instead of TabControl.ContentTemplateSelector for cached tabs + /// + public static readonly DependencyProperty TemplateSelectorProperty = + DependencyProperty.RegisterAttached("TemplateSelector", typeof(DataTemplateSelector), typeof(DiagramTabControl), new UIPropertyMetadata(null)); + + [EditorBrowsable(EditorBrowsableState.Never)] + public static TabControl GetInternalTabControl(DependencyObject obj) + { + return (TabControl)obj.GetValue(InternalTabControlProperty); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public static void SetInternalTabControl(DependencyObject obj, TabControl value) + { + obj.SetValue(InternalTabControlProperty, value); + } + + // Using a DependencyProperty as the backing store for InternalTabControl. This enables animation, styling, binding, etc... + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly DependencyProperty InternalTabControlProperty = + DependencyProperty.RegisterAttached("InternalTabControl", typeof(TabControl), typeof(DiagramTabControl), new UIPropertyMetadata(null, OnInternalTabControlChanged)); + + + [EditorBrowsable(EditorBrowsableState.Never)] + public static ContentControl GetInternalCachedContent(DependencyObject obj) + { + return (ContentControl)obj.GetValue(InternalCachedContentProperty); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public static void SetInternalCachedContent(DependencyObject obj, ContentControl value) + { + obj.SetValue(InternalCachedContentProperty, value); + } + + // Using a DependencyProperty as the backing store for InternalCachedContent. This enables animation, styling, binding, etc... + [EditorBrowsable(EditorBrowsableState.Never)] + public static readonly DependencyProperty InternalCachedContentProperty = + DependencyProperty.RegisterAttached("InternalCachedContent", typeof(ContentControl), typeof(DiagramTabControl), new UIPropertyMetadata(null)); + + [EditorBrowsable(EditorBrowsableState.Never)] + public static object GetInternalContentManager(DependencyObject obj) + { + return (object)obj.GetValue(InternalContentManagerProperty); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public static void SetInternalContentManager(DependencyObject obj, object value) + { + obj.SetValue(InternalContentManagerProperty, value); + } + + // Using a DependencyProperty as the backing store for InternalContentManager. This enables animation, styling, binding, etc... + public static readonly DependencyProperty InternalContentManagerProperty = + DependencyProperty.RegisterAttached("InternalContentManager", typeof(object), typeof(DiagramTabControl), new UIPropertyMetadata(null)); + + private static void OnIsCachedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj == null) return; + + var tabControl = obj as TabControl; + if (tabControl == null) + { + throw new InvalidOperationException("Cannot set DiagramTabControl.IsCached on object of type " + args.NewValue.GetType().Name + + ". Only objects of type TabControl can have DiagramTabControl.IsCached property."); + } + + bool newValue = (bool)args.NewValue; + + if (!newValue) + { + if (args.OldValue != null && ((bool)args.OldValue)) + { + throw new NotImplementedException("Cannot change DiagramTabControl.IsCached from True to False. Turning tab caching off is not implemented"); + } + + return; + } + + EnsureContentTemplateIsNull(tabControl); + tabControl.ContentTemplate = CreateContentTemplate(); + EnsureContentTemplateIsNotModified(tabControl); + } + + private static DataTemplate CreateContentTemplate() + { + const string xaml = + ""; + + var context = new ParserContext(); + + context.XamlTypeMapper = new XamlTypeMapper(new string[0]); + context.XamlTypeMapper.AddMappingProcessingInstruction("b", typeof(DiagramTabControl).Namespace, typeof(DiagramTabControl).Assembly.FullName); + + context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation"); + context.XmlnsDictionary.Add("b", "b"); + + var template = (DataTemplate)XamlReader.Parse(xaml, context); + return template; + } + + private static void OnInternalTabControlChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj == null) return; + var container = obj as Decorator; + + if (container == null) + { + var message = "Cannot set DiagramTabControl.InternalTabControl on object of type " + obj.GetType().Name + + ". Only controls that derive from Decorator, such as Border can have a DiagramTabControl.InternalTabControl."; + throw new InvalidOperationException(message); + } + + if (args.NewValue == null) return; + if (!(args.NewValue is TabControl)) + { + throw new InvalidOperationException("Value of DiagramTabControl.InternalTabControl cannot be of type " + args.NewValue.GetType().Name + ", it must be of type TabControl"); + } + + var tabControl = (TabControl)args.NewValue; + var contentManager = GetContentManager(tabControl, container); + contentManager.UpdateSelectedTab(); + } + + private static ContentManager GetContentManager(TabControl tabControl, Decorator container) + { + var contentManager = (ContentManager)GetInternalContentManager(tabControl); + if (contentManager != null) + { + /* + * Content manager already exists for the tab control. This means that tab content template is applied + * again, and new instance of the Border control (container) has been created. The old container + * referenced by the content manager is no longer visible and needs to be replaced + */ + contentManager.ReplaceContainer(container); + } + else + { + // create content manager for the first time + contentManager = new ContentManager(tabControl, container); + SetInternalContentManager(tabControl, contentManager); + } + + return contentManager; + } + + private static void EnsureContentTemplateIsNull(TabControl tabControl) + { + if (tabControl.ContentTemplate != null) + { + throw new InvalidOperationException("TabControl.ContentTemplate value is not null. If DiagramTabControl.IsCached is True, use DiagramTabControl.Template instead of ContentTemplate"); + } + } + + private static void EnsureContentTemplateIsNotModified(TabControl tabControl) + { + var descriptor = DependencyPropertyDescriptor.FromProperty(TabControl.ContentTemplateProperty, typeof(TabControl)); + descriptor.AddValueChanged(tabControl, (sender, args) => + { + throw new InvalidOperationException("Cannot assign to TabControl.ContentTemplate when DiagramTabControl.IsCached is True. Use DiagramTabControl.Template instead"); + }); + } + + public class ContentManager + { + TabControl _tabControl; + Decorator _border; + + public ContentManager(TabControl tabControl, Decorator border) + { + _tabControl = tabControl; + _border = border; + _tabControl.SelectionChanged += (sender, args) => { UpdateSelectedTab(); }; + } + + public void ReplaceContainer(Decorator newBorder) + { + if (Object.ReferenceEquals(_border, newBorder)) return; + + _border.Child = null; // detach any tab content that old border may hold + _border = newBorder; + } + + public void UpdateSelectedTab() + { + _border.Child = GetCurrentContent(); + } + + private ContentControl GetCurrentContent() + { + var item = _tabControl.SelectedItem; + if (item == null) return null; + + var tabItem = _tabControl.ItemContainerGenerator.ContainerFromItem(item); + if (tabItem == null) return null; + + var cachedContent = DiagramTabControl.GetInternalCachedContent(tabItem); + if (cachedContent == null) + { + cachedContent = new ContentControl + { + DataContext = item, + ContentTemplate = DiagramTabControl.GetTemplate(_tabControl), + ContentTemplateSelector = DiagramTabControl.GetTemplateSelector(_tabControl) + }; + + cachedContent.SetBinding(ContentControl.ContentProperty, new Binding()); + DiagramTabControl.SetInternalCachedContent(tabItem, cachedContent); + } + + return cachedContent; + } + } + } +} \ No newline at end of file diff --git a/AIStudio.Wpf.DiagramDesigner/Controls/DragThumb.cs b/AIStudio.Wpf.DiagramDesigner/Controls/DragThumb.cs index f9b11ee..e543d0d 100644 --- a/AIStudio.Wpf.DiagramDesigner/Controls/DragThumb.cs +++ b/AIStudio.Wpf.DiagramDesigner/Controls/DragThumb.cs @@ -213,7 +213,7 @@ namespace AIStudio.Wpf.DiagramDesigner.Controls protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e) { - base.OnPreviewMouseLeftButtonDown(e); + base.OnPreviewMouseLeftButtonUp(e); if (EditClickCount == 1) { diff --git a/AIStudio.Wpf.DiagramDesigner/Controls/MyTabControl.cs b/AIStudio.Wpf.DiagramDesigner/Controls/MyTabControl.cs deleted file mode 100644 index e50a061..0000000 --- a/AIStudio.Wpf.DiagramDesigner/Controls/MyTabControl.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; - -namespace AIStudio.Wpf.DiagramDesigner.Controls -{ - public class DiagramTabControl : TabControl - { - public DiagramTabControl() - { - // Attach event handler to TabControl.SelectionChanged event - this.SelectionChanged += DiagramTabControl_SelectionChanged; - this.IsVisibleChanged += DiagramTabControl_IsVisibleChanged; - } - - private void DiagramTabControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) - { - foreach (var item in this.Items) - { - if (item is DiagramViewModel viewModel) - { - viewModel.ShowSearch = false; - } - } - } - - private void DiagramTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - // Iterate through all TabItems and manually set the Visibility property - foreach (var item in this.Items) - { - if (item is DiagramViewModel viewModel) - { - if (item != this.SelectedItem) - { - viewModel.ShowSearch = false; - } - } - } - } - } -} \ No newline at end of file diff --git a/AIStudio.Wpf.DiagramDesigner/PathGenerators/PathGenerators.Boundary.cs b/AIStudio.Wpf.DiagramDesigner/PathGenerators/PathGenerators.Boundary.cs index b81fb9e..58ac7fc 100644 --- a/AIStudio.Wpf.DiagramDesigner/PathGenerators/PathGenerators.Boundary.cs +++ b/AIStudio.Wpf.DiagramDesigner/PathGenerators/PathGenerators.Boundary.cs @@ -33,8 +33,8 @@ namespace AIStudio.Wpf.DiagramDesigner private static PointBase[] GetRouteWithMiddlePoints(IDiagramViewModel _, ConnectionViewModel link, PointBase[] route) { var middle = GetMiddlePoints( - link.SourceConnectorInfoFully.MiddlePosition, - link.SourceConnectorInfoFully.Orientation, + link.SourceConnectorInfo.MiddlePosition, + link.SourceConnectorInfo.Orientation, link.SinkConnectorInfo.MiddlePosition, link.IsFullConnection ? link.SinkConnectorInfoFully.Orientation : (link.SinkConnectorInfo.MiddlePosition.Y >= link.SourceConnectorInfo.MiddlePosition.Y ? ConnectorOrientation.Top : ConnectorOrientation.Bottom), _.DiagramOption.LayoutOption.GridCellSize, diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramViewModel.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramViewModel.cs index e8bc873..f09e824 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramViewModel.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramViewModel.cs @@ -814,7 +814,7 @@ namespace AIStudio.Wpf.DiagramDesigner BuildMenuOptions(); } - public DiagramViewModel(DiagramItem diagramItem, string ext) : this() + public DiagramViewModel(DiagramItem diagramItem, string ext =".json") : this() { Init(diagramItem); @@ -849,6 +849,7 @@ namespace AIStudio.Wpf.DiagramDesigner public void Init(DiagramItem diagramItem) { DiagramType = diagramItem.DiagramType; + Name = diagramItem.Name; DiagramOption.LayoutOption.ShowGrid = diagramItem.ShowGrid; DiagramOption.LayoutOption.PhysicalGridCellSize = diagramItem.PhysicalGridCellSize; DiagramOption.LayoutOption.CellHorizontalAlignment = diagramItem.CellHorizontalAlignment; diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BlockViewModel/BlockDiagramViewModel.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BlockViewModel/BlockDiagramViewModel.cs index e963fb7..ecab8d6 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BlockViewModel/BlockDiagramViewModel.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BlockViewModel/BlockDiagramViewModel.cs @@ -14,6 +14,11 @@ namespace AIStudio.Wpf.DiagramDesigner { } + public BlockDiagramViewModel(DiagramItem diagramItem, string ext) : base(diagramItem, ext) + { + + } + #region private ICommand _addNextCommand; public ICommand AddNextCommand