VS2013Xml文件节点导航插件开发
一、功能描述
该插件的功能跟代码文件的导航功能类似,只是下拉框里的内容是元素的某一属性值,如图-1所示
图-1
当点击下拉框的选项后,会自动定位到该内容在xml文件的位置。此功能适用于xml文件内容较多的情况。
二、选择Editor Margin插件模板
因为该插件模板会在编辑区的底部创建一个WPF控件,如图-2所示。
图-2
而你可以创建一个WPF用户控件,并将用户控件添加到该控件里,还可以改变该控件在编辑区的位置。按照Editor Margin模板的向导建立插件项目,在项目里有三个文件:source.extension.vsixmanifest、EditorMargin1、EditorMargin1Factory,改变位置是通过EditorMargin1Factory类的MarginContainerAttribute特性实现的,该特性接收PredefinedMarginNames静态类的常量字段,这些常量字段定义了控件可以停靠的位置,如图-3所示。具体的功能主要是在EditorMargin1文件里实现。
图-3
当文档打开的时候VS会加载MarginFactory类的CreateMargin方法执行。
三、创建WPF用户控件
在项目里添加一个WPF用户控件,在用户控件里添加一个ComboBox下拉控件,当下拉框的选项改变的时候触发定位操作。由于我们是在用户控件里添加下拉控件,在用户控件外部无法监控到下拉框的改变事件,所以我们需要在用户控件里添加一个事件,在下拉框改变事件里触发该事件,这样就可以间接订阅下拉框的选项改变事件。此外,还需要对外开放一个改变下拉框宽度的函数,用于编辑区大小改变的时候可以修改下拉框的宽度。具体的代码如下所示:
/// <summary>
/// MappingInfo.xaml 的交互逻辑
/// </summary>
public partial class MappingInfo : UserControl
{
public delegate void DelegateSelectionChanged(object sender, SelectionChangedEventArgs e);
public event DelegateSelectionChanged SelectionChanged; public MappingInfo()
{
InitializeComponent();
} public MappingInfo(IEnumerable<XElement> elements)
{
InitializeComponent();
List<Elements> list = new List<Elements>();
foreach (var item in elements)
{
if (item.Attribute("name") == null)
continue;
Elements model = new Elements();
model.Value = item.Attribute("name").Value;
string desc = item.Attribute("title") != null ? item.Attribute("title").Value : item.Attribute("remark") == null ? "" : item.Attribute("remark").Value;
string cache = item.Attribute("cache") != null ? item.Attribute("cache").Value : "";
model.Text = desc != "" ? string.Format("{0}({1})", model.Value, desc) : model.Value;
if (cache != "" && cache.Equals("true", StringComparison.OrdinalIgnoreCase))
{
model.Text += " √";
}
list.Add(model);
}
cbElement.DisplayMemberPath = "Text";
cbElement.SelectedValuePath = "Value";
cbElement.ItemsSource = list;
cbElement.SelectedIndex = ;
//订阅选项改变时的事件
cbElement.SelectionChanged += cbElement_SelectionChanged;
} void cbElement_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectionChanged(sender, e);
} public void SetComboBoxWidth(double width)
{
this.cbElement.Width = width;
}
} class Elements
{
public string Text { get; set; }
public string Value { get; set; }
}
在EditorMargin1类的构造函数里将自定义的wpf用户控件添加到插件创建的控件里
//设置导航栏的相关信息
this.Height = ;
this.ClipToBounds = false;
this.Background = new SolidColorBrush(Colors.WhiteSmoke);
this.Children.Add(mapInfo);
//导航栏大小改变时改变下拉框的宽度
this.SizeChanged += Navigate_SizeChanged;
四、使用户控件自适应编辑区宽度
要实现自适应的功能只需要在XmlFileNavigation类的构造函数里订阅SizeChanged事件,由于EditorMargin1类继承了Canvas类,而Canvas类又从其他类继承了SizeChanged事件,所以只要通过this.SizeChanged就可以订阅该事件,在事件里调用创建的用户控件对外开发的修改宽度函数即可。代码如下所示:
/// <summary>
/// 大小改变时下拉框也一起调整
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Navigate_SizeChanged(object sender, SizeChangedEventArgs e)
{
//调整下拉框大小
//mapinfo为添加的wpf用户控件
mapInfo.SetComboBoxWidth(((EditorMargin1)sender).ActualWidth); }
为什么要在SizeChanged事件里设置下拉框的宽度,在EditorMargin1类的构造函数里设置就不行吗?因为在构造函数里获取编辑区宽度的话,第一个页面获取的宽度是不准确的,获取的宽度都是800,之后打开的页面的宽度才是正常的。有兴趣的同学可以在EditorMargin1类的构造函数里添加如下的代码,获取文档的宽度验证一下
EnvDTE.DTE dte=ServiceProvider.GlobalProvider.GetService(typeof(DTE)) as DTE;
double width = dte.ActiveDocument.ActiveWindow.Width;
五、根据选中的内容进行定位
由于该插件是针对xml文件的,而VS没有提供对xml文件内容的定位方法(可能是我还不知道),所以只能通过遍历整个文件来确定选中的内容是在文件中的行数。以下是在用户控件的响应事件里对选中的内容进行定位的代码:
/// <summary>
/// 下拉框改变事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void cb_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
//获取下拉框选中项
Elements model = (Elements)((ComboBox)sender).SelectedItem;
//获取DTE实例
DTE dte = ServiceProvider.GlobalProvider.GetService(typeof(DTE)) as DTE;
//找出选中项在xml文件里的行数
string[] lines = File.ReadAllLines(dte.ActiveDocument.FullName);
int line = ;
foreach (var item in lines)
{
line++;
if (item != "" && item.Contains(model.Value))
{
break;
}
}
//滚动条滚动到指定行数并显示光标
TextSelection selection = dte.ActiveDocument.Selection as TextSelection;
if (selection != null)
{
selection.MoveToLineAndOffset(line, );
selection.ActivePoint.TryToShow();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "提示", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
如果要开发的导航插件式针对cs文件的话可以通过下面的代码获取cs文件里的字段、函数、事件、属性等的相关信息:
dte.ActiveDocument.ProjectItem.FileCodeModel
以下的代码是针对ComboBox的美化样式
<UserControl.Resources>
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="" />
</Grid.ColumnDefinitions>
<Border
x:Name="Border"
Grid.ColumnSpan=""
CornerRadius=""
Background="#FCFCFC"
BorderBrush="#9BA7B7"
BorderThickness="1 1 1 1" />
<Border
Grid.Column=""
CornerRadius=""
Margin=""
Background="#FCFCFC"
BorderBrush="#9BA7B7"
BorderThickness="" />
<Path
x:Name="Arrow"
Grid.Column=""
Fill="Black"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 0 L 4 4 L 8 0 Z"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="#FDF4BF" />
<Setter TargetName="Border" Property="BorderBrush" Value="#FFEC8B" />
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="true">
<Setter TargetName="Border" Property="Background" Value="#FFEC8B" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="#EEEEEE" />
<Setter TargetName="Border" Property="BorderBrush" Value="#AAAAAA" />
<Setter Property="Foreground" Value="#888888"/>
<Setter TargetName="Arrow" Property="Fill" Value="#888888" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate> <ControlTemplate x:Key="ComboBoxTextBox" TargetType="{x:Type TextBox}">
<Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" />
</ControlTemplate> <Style x:Key="{x:Type ComboBox}" TargetType="{x:Type ComboBox}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<ToggleButton
Name="ToggleButton"
Template="{StaticResource ComboBoxToggleButton}"
Grid.Column=""
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>
<ContentPresenter
Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="5,3,23,3"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<TextBox x:Name="PART_EditableTextBox"
Style="{x:Null}"
Template="{StaticResource ComboBoxTextBox}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="5,3,23,3"
Focusable="True"
Background="Transparent"
Visibility="Hidden"
IsReadOnly="{TemplateBinding IsReadOnly}"/>
<Popup
Name="Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Slide">
<Grid
Name="DropDown"
SnapsToDevicePixels="True"
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}" >
<Border
x:Name="DropDownBorder"
Background="#EFEFEF"
BorderThickness="1 1 1 1"
CornerRadius=""
BorderBrush="Gray"/>
<ScrollViewer Margin="5,6,4,6" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value=""/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger Property="IsEditable"
Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style> <Style x:Key="{x:Type ComboBoxItem}" TargetType="{x:Type ComboBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border
Name="Border"
Padding=""
BorderThickness=""
SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<!--<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background" Value="Gray"/>
</Trigger>-->
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="#FCFCFC" />
<Setter TargetName="Border" Property="BorderBrush" Value="#E5C365" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
VS2013Xml文件节点导航插件开发的更多相关文章
- 2.6 XML配置:XML文件节点说明
来源:http://blog.csdn.net/five3/article/details/25907693 TestNG的DTD检查文件:http://testng.org/testng-1.0.d ...
- Android学习笔记(二)Manifest文件节点详解
在上一篇博文中简单介绍了Manifest文件及其存放位置,本篇就来详细介绍一下Manifest文件中的节点和一些节点的基本作用,首先看一下Manifest文件最基本的结构: <manifest ...
- linux设备驱动----利用mdev(udev)自动创建设备文件节点
1.mdev的使用方法和原理: mdev是busybox 自带的一个简化版的udev,适合于嵌入式的应用埸合.其具有使用简单的特点.它的作用,就是在系统启动和热插拔或动态加载驱动程序时,自动产生驱动程 ...
- 根据XPATH去查看修改xml文件节点的内容
首先给出xml文件解析的路径,然后去读取节点的内容. package com.inetpsa.eqc.threads; import java.util.List; import java.io.Fi ...
- 使用cat读取和echo写内核文件节点的一些问题
span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }.CodeMirror ...
- linux下自动创建设备文件节点---class
在驱动模块初始化函数中实现设备节点的自动创建 我们在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的 ...
- 如何在Sublime中打开左侧文件夹导航
Sublime中我们可以通过菜单栏的View->Side Bar->Hide Side Bar(Show Side Bar)来显示和隐藏左侧的导航栏,如下图所示. 但是,这里只会显示当前打 ...
- linux 下文件节点索引
最近发现一个奇怪的问题,就是一个pyhton 后台的服务一直打印日志文件,在中间我用vim看日志文件,关闭时习惯性的:wq退出,在此之后日志文件就不输出了. 1 对于这个现象我开始认为是python ...
- [maven] settings 文件节点配置详解
基本结构 <settings xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3. ...
随机推荐
- 什么是Kubernetes?
刚刚进学校实验室,第一次开会导师和小组同学说了n次Kubernetes,从来没听过,一脸懵逼. Kubernetes也有很多人把它叫K8S, 原文链接:http://omerio.com/2015/1 ...
- git clone, push, pull, fetch 的用法
Git是目前最流行的版本管理系统,学会Git几乎成了开发者的必备技能. Git有很多优势,其中之一就是远程操作非常简便.本文详细介绍5个Git命令,它们的概念和用法,理解了这些内容,你就会完全掌握Gi ...
- docker改变镜像源
sudo echo “DOCKER_OPTS=\”\$DOCKER_OPTS –registry-mirror=http://your-id.m.daocloud.io -d\”” >> ...
- Google SPDY
SPDY(读作“SPeeDY”)是Google开发的基于TCP的应用层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验.SPDY并不是一种用于替代HTTP的协议,而是对HTTP协议的增强 ...
- git批量删除本地分支及远程分支
1.批量删除本地分支 git branch |grep 'branchName' |xargs git branch -D git branch 查看本地分支 | grep 'branchName ...
- Spring MVC异常处理实例
以下内容引用自http://wiki.jikexueyuan.com/project/spring/mvc-framework/spring-exception-handling-example.ht ...
- Java实现敏感词过滤代码
原文:http://www.open-open.com/code/view/1445762764148 import java.io.BufferedReader; import java.io.Fi ...
- BloomFilter学习
看大数据面试题,看到BloomFilter,找了篇文章学习一下: http://www.cnblogs.com/heaad/archive/2011/01/02/1924195.html Bloom ...
- java quartz的使用,做时间轮询调用 CronTrigger
import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionExceptio ...
- Office PDF如何旋转页面之后保存
直接视图-旋转视图-逆时针,是不行的,旋转之后无法保存,另存为之后也再打开也没有效果. 要点击视图-工具-页面 然后在右边的菜单中点击旋转,然后执行旋转,然后就可以保存了.