File: System\ServiceModel\Activities\Presentation\MessageQueryEditor.xaml.cs
Project: ndp\cdf\src\NetFx40\Tools\System.Activities.Core.Presentation\System.Activities.Core.Presentation.csproj (System.Activities.Core.Presentation)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Activities.Presentation
{
    using System.Activities.Presentation.Model;
    using System.Activities.Core.Presentation;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Reflection;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Dispatcher;
    using System.Text;
    using System.Windows;
    using System.Windows.Automation;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Xml;
    using System.Xml.Linq;
    using System.ServiceModel.Description;
    using System.Runtime;
 
    partial class MessageQueryEditor
    {
        public static readonly DependencyProperty TypeCollectionProperty = DependencyProperty.Register(
            "TypeCollection",
            typeof(IList<KeyValuePair<string, Type>>), 
            typeof(MessageQueryEditor), 
            new UIPropertyMetadata(null, OnTypeCollectionChanged));
 
        static readonly DependencyPropertyKey QueryPropertyKey = DependencyProperty.RegisterReadOnly(
            "Query",
            typeof(XPathMessageQuery),
            typeof(MessageQueryEditor),
            new UIPropertyMetadata(null));
 
        static readonly DependencyProperty ActivityProperty = DependencyProperty.Register(
            "Activity",
            typeof(ModelItem),
            typeof(MessageQueryEditor),
            new UIPropertyMetadata(null));
 
        public static readonly DependencyProperty QueryProperty = QueryPropertyKey.DependencyProperty;
 
        public static readonly RoutedEvent XPathCreatedEvent = EventManager.RegisterRoutedEvent(
            "XPathCreated", 
            RoutingStrategy.Bubble, 
            typeof(RoutedEventHandler), 
            typeof(MessageQueryEditor));
      
        public MessageQueryEditor()
        {
            InitializeComponent();
        }
 
        //a collection of name and type (argument name + argument type) to expand
        public IList<KeyValuePair<string, Type>> TypeCollection
        {
            get { return (IList<KeyValuePair<string, Type>>)GetValue(TypeCollectionProperty); }
            set { SetValue(TypeCollectionProperty, value); }
        }
 
        public XPathMessageQuery Query
        {
            get { return (XPathMessageQuery)GetValue(QueryProperty); }
            private set { SetValue(QueryPropertyKey, value); }
        }
 
        internal ModelItem Activity
        {
            get { return (ModelItem)GetValue(ActivityProperty); }
            set { SetValue(ActivityProperty, value); }
        }
 
        //event raised whenever user creates a xpath
        public event RoutedEventHandler XPathCreated
        {
            add { this.AddHandler(XPathCreatedEvent, value); }
            remove { this.RemoveHandler(XPathCreatedEvent, value); }
        }
 
        //override default combo box item with my own implementation and style
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new MessageQueryComboBoxItem() { Style = (Style)this.FindResource("comboBoxStyle") };
        }
 
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.Key == Key.Enter && Keyboard.Modifiers == ModifierKeys.None)
            {
                e.Handled = true;
                if (!this.IsDropDownOpen && !string.IsNullOrEmpty(this.Text))
                {
                    this.Query = new XPathMessageQuery(this.Text);
                    this.RaiseEvent(new RoutedEventArgs(XPathCreatedEvent, this));
                }
            }
            base.OnKeyDown(e);
        }
 
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            if (!LocalAppContextSwitches.UseLegacyAccessibilityFeatures)
            {
                this.SetValue(AutomationProperties.NameProperty, this.Resources["MessageQueryEditorAutomationName"]);
                if (this.IsEditable && this.Template != null)
                {
                    var textBox = this.Template.FindName("PART_EditableTextBox", this) as TextBox;
                    if (textBox != null)
                    {
                        textBox.SetValue(AutomationProperties.NameProperty, this.GetValue(AutomationProperties.NameProperty));
                    }
                }
            }
        }
 
        //user double clicked on the expanded type, create a xpath
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
            Justification = "Propagating exceptions might lead to VS crash.")]
        [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
            Justification = "Propagating exceptions might lead to VS crash.")]
        void OnTypeSelectionChanged(object sender, RoutedEventArgs e)
        {
            var contentCorrelationDesigner = (ContentCorrelationTypeExpander)sender;
            //is selection valid (valid type or property)
            if (contentCorrelationDesigner.IsSelectionValid)
            {
                var path = contentCorrelationDesigner.GetMemberPath();
                var type = contentCorrelationDesigner.GetSelectedType();
                try
                {
                    XmlNamespaceManager namespaceManager = null;
                    string xpathQuery = string.Empty;
                    var content = this.Activity.Properties["Content"].Value;
                    if (content.IsAssignableFrom<ReceiveMessageContent>() || content.IsAssignableFrom<SendMessageContent>())
                    {
                        //generating xpath for message content
                        xpathQuery = XPathQueryGenerator.CreateFromDataContractSerializer(type, path, out namespaceManager);
                    }
                    else
                    {
                        //generating xpath for parameter content
                        XName serviceContractName = null;
                        string operationName = null;
                        string parameterName = contentCorrelationDesigner.SelectedTypeEntry.Name;
                        bool isReply = this.Activity.IsAssignableFrom<SendReply>() || this.Activity.IsAssignableFrom<ReceiveReply>();
                        if (isReply)
                        {
                            operationName = (string)this.Activity.Properties["Request"].Value.Properties["OperationName"].ComputedValue;
                            serviceContractName = (XName)this.Activity.Properties["Request"].Value.Properties["ServiceContractName"].ComputedValue;
 
                            if (string.IsNullOrEmpty(operationName) || null == serviceContractName)
                            {
                                ModelItem requestDisplayName;
                                this.Activity.TryGetPropertyValue(out requestDisplayName, "Request", "DisplayName");
                                throw FxTrace.Exception.AsError(new InvalidOperationException(
                                        string.Format(CultureInfo.CurrentUICulture, (string)this.FindResource("parametersRequiredText"), requestDisplayName.GetCurrentValue())));
                            }
                        }
                        else
                        {
                            operationName = (string)this.Activity.Properties["OperationName"].ComputedValue;
                            serviceContractName = (XName)this.Activity.Properties["ServiceContractName"].ComputedValue;
 
                            if (string.IsNullOrEmpty(operationName) || null == serviceContractName)
                            {
                                throw FxTrace.Exception.AsError(new InvalidOperationException(
                                        string.Format(CultureInfo.CurrentUICulture, (string)this.FindResource("parametersRequiredText"), this.Activity.Properties["DisplayName"].ComputedValue)));
                            }
                        }
                        xpathQuery = ParameterXPathQueryGenerator.CreateFromDataContractSerializer(serviceContractName, operationName, parameterName, isReply, type, path, out namespaceManager);
                    }
                    //use CDF api to build a xpath out of type and its properties
                    string xpath = string.Format(CultureInfo.InvariantCulture, "sm:body(){0}", xpathQuery);
 
                    //get the context
                    //We need to copy over the namespaces from the manager's table 1 by 1. According to MSDN:
                    //If you specify an existing name table, any namespaces in the name table are not automatically added to XmlNamespaceManager.
                    //You must use AddNamespace and RemoveNamespace to add or remove namespaces.
                    XPathMessageContext messageContext = new XPathMessageContext();
                    foreach (string prefix in namespaceManager)
                    {
                        if (!string.IsNullOrEmpty(prefix) && !messageContext.HasNamespace(prefix) && prefix != "xmlns")
                        {
                            messageContext.AddNamespace(prefix, namespaceManager.LookupNamespace(prefix));
                        }
                    }
 
                    var typeEntry = (ExpanderTypeEntry)contentCorrelationDesigner.Tag;
                    //construct xpath 
                    XPathMessageQuery query = new XPathMessageQuery(xpath, messageContext);
                    //store the xpath in the Tag property; this combo's selectedValue is bound to i
                    typeEntry.Tag = query;
                    this.SelectedIndex = 0;
                    this.IsDropDownOpen = false;
                    this.Query = query;
                    this.RaiseEvent(new RoutedEventArgs(XPathCreatedEvent, this));
                }
                catch (Exception err)
                {
                    MessageBox.Show(
                        err.Message,
                        (string)this.Resources["controlTitle"],
                        MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }
 
        void OnTypeCollectionChanged()
        {
            //user provided a list of types to expand
            var items = new List<ExpanderTypeEntry>();
            if (null != this.TypeCollection)
            {
                StringBuilder text = new StringBuilder((string)this.FindResource("selectedDisplayText"));
                bool addComma = false;
                //copy all of then into ExpanderTypeEntry
                foreach (var entry in this.TypeCollection)
                {
                    if (addComma)
                    {
                        text.Append(", ");
                    }
                    items.Add(new ExpanderTypeEntry() { TypeToExpand = entry.Value, Name = entry.Key });
                    text.Append(entry.Key);
                    addComma = true;
                }
                //requirement of combo box is that data source must be enumerable, so provide one elemnt array 
                this.ItemsSource = new object[] { new TypeEntryContainer() { Items = items, DisplayText = text.ToString() } };
            }
            else
            {
                this.ItemsSource = null;
            }
        }
 
        static void OnTypeCollectionChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            ((MessageQueryEditor)sender).OnTypeCollectionChanged();
        }
 
        //ComboBox item subclass 
        sealed class MessageQueryComboBoxItem : ComboBoxItem
        {
            public MessageQueryComboBoxItem()
            {
                //i don't want it to be focusable - its conent is
                this.Focusable = false;
            }
 
            //do not notify parent ComboBox about mouse down & up events - i don't want to close popup too early
            //i handle closing myself
            protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
            {                
            }
            protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
            {
            }
        }
 
    }
 
}