如何确定某项是否是 WPF ItemTemplate 中的最后一项?

发布于 2024-08-26 10:13:07 字数 1216 浏览 10 评论 0原文

我有一些

<ItemsControl Name="mItemsControl">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding Mode=OneWay}" KeyUp="TextBox_KeyUp"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

绑定到简单 ObservableCollection 的

private ObservableCollection<string> mCollection = new ObservableCollection<string>();

public MainWindow()
{
    InitializeComponent();

    this.mCollection.Add("Test1");
    this.mCollection.Add("Test2");
    this.mItemsControl.ItemsSource = this.mCollection;
}

XAML在 last 文本框中按 Enter 键后,我希望出现另一个文本框。我有代码可以做到这一点,但有一个差距:

private void TextBox_KeyUp(object sender, KeyEventArgs e)
{
    if (e.Key != Key.Enter)
    {
        return;
    }

    TextBox textbox = (TextBox)sender;

    if (IsTextBoxTheLastOneInTheTemplate(textbox))
    {
        this.mCollection.Add("A new textbox appears!");
    }
}

函数 IsTextBoxTheLastOneInTheTemplate() 是我需要的东西,但不知道如何编写。我该如何去写呢?

我考虑过使用 ItemsControl.ItemContainerGenerator,但无法将所有部分放在一起。

谢谢!

-麦克风

I have some XAML

<ItemsControl Name="mItemsControl">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding Mode=OneWay}" KeyUp="TextBox_KeyUp"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

that's bound to a simple ObservableCollection

private ObservableCollection<string> mCollection = new ObservableCollection<string>();

public MainWindow()
{
    InitializeComponent();

    this.mCollection.Add("Test1");
    this.mCollection.Add("Test2");
    this.mItemsControl.ItemsSource = this.mCollection;
}

Upon hitting the enter key in the last TextBox, I want another TextBox to appear. I have code that does it, but there's a gap:

private void TextBox_KeyUp(object sender, KeyEventArgs e)
{
    if (e.Key != Key.Enter)
    {
        return;
    }

    TextBox textbox = (TextBox)sender;

    if (IsTextBoxTheLastOneInTheTemplate(textbox))
    {
        this.mCollection.Add("A new textbox appears!");
    }
}

The function IsTextBoxTheLastOneInTheTemplate() is something that I need, but can't figure out how to write. How would I go about writing it?

I've considered using ItemsControl.ItemContainerGenerator, but can't put all the pieces together.

Thanks!

-Mike

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

执笔绘流年 2024-09-02 10:13:07

我假设这是您正在处理的事情的简化版本。单向绑定到字符串集合的文本框对我来说没有意义。

本例中的主要问题是使用简单的字符串作为项目源。我假设我们不能保证字符串是唯一的,所以我们不能从 textbox.Text 得出任何结论。另外,由于字符串是不可变的,因此我们不能使用字符串的实例来推断任何内容。

解决方案的第一步是创建一个类来保存我们可以引用的数据。 (在这种情况下,这似乎有点愚蠢,因为它只包含一个字符串。)

    class MyData
    {
        public string Value { get; set; }
    }

您的第二个代码块变为:

    ObservableCollection<MyData> mCollection = new ObservableCollection<MyData>();

    public MainWindow()
    {
        InitializeComponent();

        this.mCollection.Add(new MyData { Value = "Test1" });
        this.mCollection.Add(new MyData { Value = "Test2" });
        this.mItemsControl.ItemsSource = this.mCollection;
    }

我们将使用文本框的 Tag 属性来存储对绑定源的引用。我们将用它来解决唯一性问题。 XAML 变为:

    <ItemsControl Name="mItemsControl">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBox Text="{Binding Value}" Tag="{Binding}" KeyUp="TextBox_KeyUp"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

最后,处理程序变为:

    private void TextBox_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.Key != Key.Enter)
        {
            return;
        }

        TextBox textbox = (TextBox)sender;

        if (mItemsControl.Items.IndexOf(textbox.Tag) == mItemsControl.Items.Count - 1)
        {
            this.mCollection.Add(new MyData() { Value = "A new textbox appears!" });
        }
    }

I'm assuming this is a simplified version of what you're working on. A textbox with oneway binding to a string collection doesn't make sense to me.

The main problem in this case is using a simple string as the item source. I'm assuming we can't guarantee the strings will be unique so we can't draw any conclusions from textbox.Text. Also, since strings are immutable, we can't use the instance of the string to infer anything.

The first step in the solution is to create a class to hold the data that we can refer to. (This seems a little silly in this case as it just holds a string.)

    class MyData
    {
        public string Value { get; set; }
    }

Your second code block becomes:

    ObservableCollection<MyData> mCollection = new ObservableCollection<MyData>();

    public MainWindow()
    {
        InitializeComponent();

        this.mCollection.Add(new MyData { Value = "Test1" });
        this.mCollection.Add(new MyData { Value = "Test2" });
        this.mItemsControl.ItemsSource = this.mCollection;
    }

We'll use the Tag property of the textbox to store a reference to our binding source. We'll use this to get around the uniqueness issues. The XAML becomes:

    <ItemsControl Name="mItemsControl">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBox Text="{Binding Value}" Tag="{Binding}" KeyUp="TextBox_KeyUp"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

Lastly, the handler becomes:

    private void TextBox_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.Key != Key.Enter)
        {
            return;
        }

        TextBox textbox = (TextBox)sender;

        if (mItemsControl.Items.IndexOf(textbox.Tag) == mItemsControl.Items.Count - 1)
        {
            this.mCollection.Add(new MyData() { Value = "A new textbox appears!" });
        }
    }
黑色毁心梦 2024-09-02 10:13:07

我能够通过参考 获得一个不错的解决方案http://drwpf.com/blog/2008/07/20/itemscontrol-g-is-for-generator/。不是超级优雅,但它对我有用。

    private void TextBox_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.Key != Key.Enter)
        {
            return;
        }

        TextBox textbox = (TextBox)sender;

        var lastContainer = this.mItemsControl.ItemContainerGenerator.ContainerFromIndex(this.mItemsControl.Items.Count - 1);

        var visualContainer = (Visual)lastContainer;

        var containedTextbox = (TextBox)GetDescendantByType(visualContainer, typeof(TextBox));

        var isSame = textbox == containedTextbox;

        if (isSame)
        {
             this.mCollection.Add("A new textbox appears!");
        }
    }


    public static Visual GetDescendantByType(Visual element, Type type)
    {
        if (element.GetType() == type) return element;

        Visual foundElement = null;

        if (element is FrameworkElement)
            (element as FrameworkElement).ApplyTemplate();

        for (int i = 0;
            i < VisualTreeHelper.GetChildrenCount(element); i++)
        {
            Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
            foundElement = GetDescendantByType(visual, type);
            if (foundElement != null)
                break;
        }

        return foundElement;
    }

I was able to get a decent solution by referring to http://drwpf.com/blog/2008/07/20/itemscontrol-g-is-for-generator/. Not super-elegant, but it worked for me.

    private void TextBox_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.Key != Key.Enter)
        {
            return;
        }

        TextBox textbox = (TextBox)sender;

        var lastContainer = this.mItemsControl.ItemContainerGenerator.ContainerFromIndex(this.mItemsControl.Items.Count - 1);

        var visualContainer = (Visual)lastContainer;

        var containedTextbox = (TextBox)GetDescendantByType(visualContainer, typeof(TextBox));

        var isSame = textbox == containedTextbox;

        if (isSame)
        {
             this.mCollection.Add("A new textbox appears!");
        }
    }


    public static Visual GetDescendantByType(Visual element, Type type)
    {
        if (element.GetType() == type) return element;

        Visual foundElement = null;

        if (element is FrameworkElement)
            (element as FrameworkElement).ApplyTemplate();

        for (int i = 0;
            i < VisualTreeHelper.GetChildrenCount(element); i++)
        {
            Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
            foundElement = GetDescendantByType(visual, type);
            if (foundElement != null)
                break;
        }

        return foundElement;
    }
桜花祭 2024-09-02 10:13:07

在我看来,这是在视图模型中最好定义的行为:

public class ItemCollection : ObservableCollection<Item>
{
    public ItemCollection()
    {
        // this guarantees that any instance created always has at least one
        // item in it - you don't need this if you're creating instances in
        // code, but if you just create them in XAML you do.
        Item item = new Item(this);
        Add(item);
    }
}

public class Item
{
    internal Item(ItemCollection owner)
    {
        Owner = owner;
    }

    public bool IsLast
    {
        get
        {
            return Owner.LastOrDefault() == this;
        }
    }

    private ItemCollection Owner { get; set; }

    private string _Value;

    // here's the actual behavior:  if the last item in the collection is
    // given a non-empty Value, a new item gets added after it.
    public string Value
    {
        get { return _Value; }
        set
        {
            _Value = value;
            if (IsLast && !String.IsNullOrEmpty(_Value))
            {
                Owner.Add(new Item(Owner));
            }
        }
    }
}

从这里开始,当用户按下 ENTER 时,让 TextBox 更新其源代码是一个简单的事情:

<DataTemplate DataType="{x:Type local:Item}">
    <TextBox Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
             KeyUp="TextBox_KeyUp"/>
</DataTemplate>

使用 KeyUp代码> 事件处理程序:

private void TextBox_KeyUp(object sender, KeyEventArgs e)
{
    if (e.Key != Key.Enter)
    {
        return;
    }

    TextBox t = (TextBox)sender;
    BindingExpression be = t.GetBindingExpression(TextBox.TextProperty);
    be.UpdateSource();
}

It seems to me that this is behavior best defined in the view model:

public class ItemCollection : ObservableCollection<Item>
{
    public ItemCollection()
    {
        // this guarantees that any instance created always has at least one
        // item in it - you don't need this if you're creating instances in
        // code, but if you just create them in XAML you do.
        Item item = new Item(this);
        Add(item);
    }
}

public class Item
{
    internal Item(ItemCollection owner)
    {
        Owner = owner;
    }

    public bool IsLast
    {
        get
        {
            return Owner.LastOrDefault() == this;
        }
    }

    private ItemCollection Owner { get; set; }

    private string _Value;

    // here's the actual behavior:  if the last item in the collection is
    // given a non-empty Value, a new item gets added after it.
    public string Value
    {
        get { return _Value; }
        set
        {
            _Value = value;
            if (IsLast && !String.IsNullOrEmpty(_Value))
            {
                Owner.Add(new Item(Owner));
            }
        }
    }
}

From here, it's a simple matter of making the TextBox update its source when the user presses ENTER:

<DataTemplate DataType="{x:Type local:Item}">
    <TextBox Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
             KeyUp="TextBox_KeyUp"/>
</DataTemplate>

With the KeyUp event handler:

private void TextBox_KeyUp(object sender, KeyEventArgs e)
{
    if (e.Key != Key.Enter)
    {
        return;
    }

    TextBox t = (TextBox)sender;
    BindingExpression be = t.GetBindingExpression(TextBox.TextProperty);
    be.UpdateSource();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文