代码之家  ›  专栏  ›  技术社区  ›  RationalGeek

以编程方式为Silverlight组合框创建ItemsPanelTemplate?

  •  2
  • RationalGeek  · 技术社区  · 14 年前

    我正在尝试创建与组合框相关的混合行为。为了得到我想要的效果,组合框的ItemsPanel必须添加一个特定的元素。我不想在每个使用行为的组合框中都这样做,所以我希望行为能够以编程方式注入ItemsPanelTemplate。不过,我似乎找不到办法。ItemsPanelTemplate似乎没有允许我设置可视化树的属性/方法。WPF ItemsPanelTemplate具有VisualTree,但Silverlight没有。

    基本上,这个XAML的编程等价物是什么?

        <ComboBox>
            <ComboBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel/>
                </ItemsPanelTemplate>
            </ComboBox.ItemsPanel>
        </ComboBox>
    

    编辑:
    好吧,显然这不是一个简单的问题,所以我开始悬赏,我会给一些更多的背景,以防有另一种方法来解决这个问题。我想为Silverlight ComboBox提供键盘支持。开箱即用它只支持向上和向下箭头,但我也希望它工作,这样当用户点击一个字母时,组合框跳到该字母的第一项,类似于组合框在浏览器或Windows应用程序中的工作方式。

    我发现 this blog post ,这让我半途而废。调整该行为代码后,组合框将根据字母输入更改选择。 ,当组合框打开时不起作用。原因,根据 this blog post

    所以这就是促使我提出如何将StackPanel放入组合框的ItemsPanelTemplate的原因, . 如果这是不可能的,有没有其他的方法让它发挥作用?是的,我知道我可以转到应用程序中的每个组合框并添加StackPanel和事件。但我想通过一个行为来实现这一点,这样我就不必修改应用程序中的每个组合框,这样我就可以跨应用程序重用这个逻辑。

    下面AnthonyWJones使用XamlReader给出的答案让我有了一部分收获,因为我可以创建StackPanel并将其放入模板中。但是,为了订阅事件,我需要能够以编程方式访问该SP。

    4 回复  |  直到 6 年前
        1
  •  1
  •   Vladimir Dorokhov    14 年前

    我认为,对您来说最好的方法-扩展组合框功能不是通过行为而是使用继承。 因此,您可以创建自己的控件MyComboBox:ComboBox。为其创建样式-获取默认组合框样式 here

    改为写入(按名称查找ScrollViewer):

    <ScrollViewer x:Name=“ScrollViewer”BorderThickness=“0”Padding=“1”>

       < ItemsPresenter />
    

    </滚动查看器>

    <ScrollViewer x:Name=“ScrollViewer”BorderThickness=“0”Padding=“1”>

    < StackPanel x:Name="StackPanel" >
    
       < ItemsPresenter />
    
    < /StackPanel >
    

    </滚动查看器>

    公共类MyComboBox:组合框{

        public CM()
        {
            DefaultStyleKey = typeof (MyComboBox);
        }
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            StackPanel stackPanel = (StackPanel)GetTemplateChild("StackPanel");
            stackPanel.KeyUp += (s, e) => { /*do something*/ };
        }
    

    }

    继承更有力。它允许使用模板元素。

    1) 在注入面板上保留引用的代码是不可能的。
    2) 为了获得对注入面板的引用,该面板必须在某些存储器中注册,例如。

    <组合框>

       < ComboBox.ItemsPanel>
    
           < ItemsPanelTemplate>
    
               < StackPanel>
    
                 < i:Interaction.EventTriggers>
    
                   < i:EventTrigger EventName="Loaded">
    
                      < RegisterMyInstanceInAccessibleFromCodePlaceAction/>
    
                   < /i:EventTrigger>
    
                 < /i:Interaction.EventTriggers>
    
              < /StackPanel>
    
           < /ItemsPanelTemplate>
    
       < /ComboBox.ItemsPanel>
    

    </组合框>

        2
  •  6
  •   Adam Barney    14 年前

    这应该管用。我已经演示了如何改变下面的方向。您可以添加其他SetValue调用来修改其他属性。

    cb.ItemsPanel = new ItemsPanelTemplate();
    var stackPanelFactory = new FrameworkElementFactory(typeof (StackPanel));
    // Modify it like this:
    stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
    // Set the root of the template to the stack panel factory:
    cb.ItemsPanel.VisualTree = stackPanelFactory;
    

    您可以在本文中找到更详细的信息: http://www.codeproject.com/KB/WPF/codeVsXAML.aspx

        3
  •  4
  •   AnthonyWJones    14 年前

    实际上,您想要以编程方式构建的是:-

    <ItemsPanelTemplate>
        <StackPanel />
    </ItemsPanelTemplate>
    

    ItemsPanel 财产 ComboBox 它附属于。目前,您的行为是纯代码,但没有办法纯粹在代码中创建上述内容。

    由于这是Xaml的一小部分,最简单的方法是使用XamlReader:-

    ItemsPanelTemplate itemsPanelTemplate = XamlReader.Load("<ItemsPanelTemplate><StackPanel /></ItemsPanelTemplate>");
    
        4
  •  0
  •   JoeBrockhaus ozkary    13 年前

    我在试图从代码设置ItemsPanel以便实现virtualizengstackpanel时找到了您的文章。当我的单子上有成百上千个项目时,表现就糟透了。不管怎样。。我就是这样做的。

    1) 自定义控件
    --您也可以将此行为应用于普通组合框-在每个实例上,或通过样式。

    --您还可以公开超时值,以便在xaml中重写。。
    --而且,当下拉列表本身打开时,这似乎不起作用。不知道为什么。。从没调查过

    1。。

    public class KeyPressSelectionComboBox : ComboBox
    { 
    
       private BindingExpression _bindingExpression;  
    
    
        public KeyPressSelectionComboBox()
            : base()
        {
            Interaction.GetBehaviors(this).Add(new KeyPressSelectionBehavior());
            Height = 22;
    
            this.SelectionChanged += new SelectionChangedEventHandler(KeyPressSelectionComboBox_SelectionChanged);
        }
    
        void KeyPressSelectionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (_bindingExpression == null)
            {
                _bindingExpression = this.GetBindingExpression(ComboBox.SelectedValueProperty);
            }
            else
            {
                if (this.GetBindingExpression(ComboBox.SelectedValueProperty) == null)
                {
                    this.SetBinding(ComboBox.SelectedValueProperty, _bindingExpression.ParentBinding);
                }
            }
        }  
    
    }
    

    2。。。

    /// <summary>
    /// This behavior can be attached to a ListBox or ComboBox to 
    /// add keyboard selection
    /// </summary>
    public class KeyPressSelectionBehavior : Behavior<Selector>
    {
    
        private string keyDownHistory = string.Empty;
        private double _keyDownTimeout = 2500;
    
        private DateTime _lastKeyDownTime;
        private DateTime LastKeyDownTime
        {
            get
            {
                if (this._lastKeyDownTime == null)
                    this._lastKeyDownTime = DateTime.Now;
    
                return this._lastKeyDownTime;
            }
            set { _lastKeyDownTime = value; }
        }
    
    
        /// <summary>
        /// Gets or sets the Path used to select the text
        /// </summary>
        public string SelectionMemberPath { get; set; }
    
        /// <summary>
        /// Gets or sets the Timeout (ms) used to reset the KeyDownHistory item search string
        /// </summary>
        public double KeyDownTimeout
        {
            get { return _keyDownTimeout; }
            set { _keyDownTimeout = value; }
        }
    
    
        public KeyPressSelectionBehavior() { }
    
        /// <summary>
        /// Attaches to the specified object: subscribe on KeyDown event
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.KeyDown += DoKeyDown;
        }
    
        void DoKeyDown(object sender, KeyEventArgs e)
        {
            // Create a list of strings and indexes
            int index = 0;
            IEnumerable<Item> list = null;
    
            var path = SelectionMemberPath ??
                this.AssociatedObject.DisplayMemberPath;
            var evaluator = new BindingEvaluator();
    
            if (path != null)
            {
                list = this.AssociatedObject.Items.OfType<object>()
                    .Select(item =>
                    {
                        // retrieve the value using the supplied Path
                        var binding = new Binding();
                        binding.Path = new PropertyPath(path);
                        binding.Source = item;
    
                        BindingOperations.SetBinding(evaluator,
                            BindingEvaluator.TargetProperty, binding);
                        var value = evaluator.Target;
    
                        return new Item
                        {
                            Index = index++,
                            Text = Convert.ToString(value)
                        };
                    });
            }
            else
            {
                list = this.AssociatedObject.Items.OfType<ListBoxItem>()
                    .Select(item => new Item
                    {
                        Index = index++,
                        Text = Convert.ToString(item.Content)
                    });
            }
            // Sort the list starting at next selectedIndex to the end and 
            // then from the beginning
            list = list.OrderBy(item => item.Index <=
                this.AssociatedObject.SelectedIndex ?
                item.Index + this.AssociatedObject.Items.Count : item.Index);
    
            // calculate how long has passed since the user typed a letter
            var elapsedTime = DateTime.Now - this.LastKeyDownTime;
            if (elapsedTime.TotalMilliseconds <= this.KeyDownTimeout)
            { /* if it's less than the timeout, add to the search string */
                this.keyDownHistory += GetKeyValue(e);
            }
            else
            { /* otherwise replace it */
                this.keyDownHistory = GetKeyValue(e);
            }
    
            // Find first starting with the search string            
            var searchText = this.keyDownHistory;
            var first = list.FirstOrDefault(item => 
                item.Text.StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase));
    
            if (first != null)
            { /* found */
                this.AssociatedObject.SelectedIndex = first.Index;
            }
            else
            { /* not found - so reset the KeyDownHistory */
                this.keyDownHistory = string.Empty;
            }
    
    
            // reset the last time a key was pressed
            this.LastKeyDownTime = DateTime.Now;
        }
    
        /// <summary>
        /// Gets the value of the pressed key, 
        /// specifically converting number keys from their "Dx" value to their expected "x" value
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        private static string GetKeyValue(KeyEventArgs e)
        {
            string rValue = string.Empty;
    
            switch (e.Key)
            {
                default:
                    rValue = e.Key.ToString();
                    break;
                case Key.D0:
                case Key.NumPad0:
                    rValue = (0).ToString();
                    break;
                case Key.D1:
                case Key.NumPad1:
                    rValue = (1).ToString();
                    break;
                case Key.D2:
                case Key.NumPad2:
                    rValue = (2).ToString();
                    break;
                case Key.D3:
                case Key.NumPad3:
                    rValue = (3).ToString();
                    break;
                case Key.D4:
                case Key.NumPad4:
                    rValue = (4).ToString();
                    break;
                case Key.D5:
                case Key.NumPad5:
                    rValue = (5).ToString();
                    break;
                case Key.D6:
                case Key.NumPad6:
                    rValue = (6).ToString();
                    break;
                case Key.D7:
                case Key.NumPad7:
                    rValue = (7).ToString();
                    break;
                case Key.D8:
                case Key.NumPad8:
                    rValue = (8).ToString();
                    break;
                case Key.D9:
                case Key.NumPad9:
                    rValue = (9).ToString();
                    break;
    
            }
    
            return rValue;
        }
    
        /// <summary>
        /// Helper class
        /// </summary>
        private class Item
        {
            public int Index;
            public string Text;
        }
    
        /// <summary>
        /// Helper class used for property path value retrieval
        /// </summary>
        private class BindingEvaluator : FrameworkElement
        {
    
            public static readonly DependencyProperty TargetProperty =
                DependencyProperty.Register(
                    "Target",
                    typeof(object),
                    typeof(BindingEvaluator), null);
    
            public object Target
            {
                get { return GetValue(TargetProperty); }
                set { SetValue(TargetProperty, value); }
            }
        }
    
    }