将事件绑定到wpf列表以进行实时更新



我正在构建一个WPF应用程序,它将填充来自各种新闻服务的过滤标题。每个标题都会触发一个事件,我可以在控制台应用程序中显示该事件。我想在这里使用WPF,但在此之前已经有机器人使用过了。我的主窗口xaml如下所示。我最初的想法是让ObservableCollection在xaml的列表视图中填充列表项。如果这不是正确的方法,我愿意接受专家对更好方式的意见,因为显示收据的速度至关重要。如果我所做的是正确的,那么我如何将ObservableCollection的新条目绑定到要显示的新列表项?

<StackPanel Orientation="Vertical" Margin="5,150 5 50" Name="HeadlinePanel">
        <TextBlock Text="Filtered Headlines From Monitoring List" 
       HorizontalAlignment="Left" Margin="0,0 5 5" Name="ScrollingHeadlineLabel" FontWeight="Bold" FontSize="14" Background="LightSkyBlue" />
        <ListBox>                
            <ListBoxItem>
                <StackPanel Orientation="Horizontal">
                    <Image Source="a property on the headline" />
                    <TextBlock><Run Text="headline is from a website"/></TextBlock>
                </StackPanel>
            </ListBoxItem>
            <ListBoxItem>
                <StackPanel Orientation="Horizontal">
                    <Image Source="a property on the headline" />
                    <TextBlock><Run Text="headline is from TWTR"/></TextBlock>
                </StackPanel>
            </ListBoxItem>
            <ListBoxItem>
                <StackPanel Orientation="Horizontal">
                    <Image Source="a property on the headline" />
                    <TextBlock><Run Text="headline from a different website"/></TextBlock>
                </StackPanel>
            </ListBoxItem>
            <ListBoxItem>
                <StackPanel Orientation="Horizontal">
                    <Image Source="a property on the headline" />
                    <TextBlock><Run Text="text from a different tweet"/></TextBlock>
                </StackPanel>
            </ListBoxItem>               
        </ListBox>
    </StackPanel>

在控制台应用程序中,流在filteredStream.Start()中开始(代码如下),但处理程序需要先注册。在控制台应用程序中,我可以向控制台写入(注释掉),但在这里,当事件触发时,我会将标题对象添加到集合中。我的问题是如何将其绑定到我的xaml列表项。我将从主窗口方法启动流?或者我创建的某种方法在其中运行?

 var config = new TwitterOAuthConfig()
        {
            ConsumerKey = customerKey,
            ConsumerSecret = customerSecret,
            AccessToken = accessToken,
            AccessTokenSecret = accessTokenSecret,
            GeoOnly = false,
            KeywordsToMonitor = keywords,
            UsersToFollow = followers
        };
        var filteredStream = new TwitterClient(config);
        var headlineCollection = new ObservableCollection<Headline>();
        // subscribe to the event handler
        filteredStream.HeadlineReceivedEvent +=
            (sender, arguments) => headlineCollection.Add(arguments.Headline);
                //Console.WriteLine("ID: {0} said {1}", arguments.Headline.Username, arguments.Headline.HeadlineText);

        filteredStream.ExceptionReceived += (sender, exception) => Console.WriteLine(exception.HeadlineException.ResponseMessage);
        filteredStream.Start();

这是我的原始标题ViewModel

 public class HeadlineViewModel : ObservableItem
{
    private string _headlineText;
    public string Source { get; set; }
    public string Username { get; set; }
    public string Text
    {
        get { return _headlineText; }
        set
        {
            _headlineText = value;
            RaisePropertyChangedEvent("HeadlineText");
        }
    }
    public List<string> UrlsParsedFromText { get; set; }
    public string TimeStamp { get; set; }
}

我已将其更新为以下内容:

public class HeadlineViewModel 
{
    public class HeadlineDisplayItems: ObservableItem
    {
        private string _headlineText;
        public string HeadlineIconPath { get; set; }
        public string TimeStamp { get; set; }
        public string Username { get; set; }
        public string Text
        {
            get { return _headlineText; }
            set
            {
                _headlineText = value;
                RaisePropertyChangedEvent("HeadlineText");
            }
        }
    }
    public List<string> UrlsParsedFromText { get; set; }
    public ObservableCollection<HeadlineDisplayItems> HeadlineCollection { get; set; }
}

我不知道你的体系结构,但wpf主要用于他们所称的MVVM(模型-视图-视图-模型),其中你有你的视图(你已经发布了代码)、ViewModel(我相信你没有)和模型(这就是你正在使用的标题)。ViewModel的目标是简化视图的使用寿命,并提供显示所需的所有信息和操作。

例如,您应该为正在构建的整个视图创建一个ViewModel,比如"HeadlinePanelViewModel"(我不建议使用名称中的面板,因为使用ViewModel的想法是抽象正在使用的控件或技术)。HeadlinePanelViewModel需要使标题可用,因此它必须有一个ViewModel集合,表示与标题相关的所有信息(图标、标题、链接…)。最后,您有一个包含ObservableCollection的HeadlinePanel ViewModel。将其设置为视图的DataContext,您必须准备好显示您的信息。

现在是实际加载信息的部分。再说一遍,我不知道你的架构。但简单来说,您可以在HeadlinePanelViewModel中实例化filteredStream,每次触发HeadlineReceivedEvent时,您都会创建一个与之对应的HeadlineViewModel并添加到您的集合中。

基于您答案中的代码的"完整"代码:

ViewModel:

public class HeadlineViewModel 
{
    public HeadlineViewModel()
    {
        // This is here only for simplicity. Put elsewhere
        var config = new TwitterOAuthConfig()
        {
            ConsumerKey = customerKey,
            ConsumerSecret = customerSecret,
            AccessToken = accessToken,
            AccessTokenSecret = accessTokenSecret,
            GeoOnly = false,
            KeywordsToMonitor = keywords,
            UsersToFollow = followers
        };
        var filteredStream = new TwitterClient(config);
        HeadlineCollection = new ObservableCollection<HeadlineDisplayItems>();
        // subscribe to the event handler
        filteredStream.HeadlineReceivedEvent +=
            (sender, arguments) =>     HeadlineCollection.Add(ConvertToViewModel(arguments.Headline));
                //Console.WriteLine("ID: {0} said {1}",     arguments.Headline.Username, arguments.Headline.HeadlineText);

        filteredStream.ExceptionReceived += (sender, exception) =>     Console.WriteLine(exception.HeadlineException.ResponseMessage);
        filteredStream.Start();
    }
    private HeadlineDisplayItems ConvertToViewModel(Headline headline)
    {
        // Conversion code here
    }
    public class HeadlineDisplayItems: ObservableItem
    {
        private string _headlineText;
        public string HeadlineIconPath { get; set; }
        public string TimeStamp { get; set; }
        public string Username { get; set; }
        public string Text
        {
            get { return _headlineText; }
            set
            {
                _headlineText = value;
                RaisePropertyChangedEvent("HeadlineText");
            }
        }
    }
    public List<string> UrlsParsedFromText { get; set; }
    public ObservableCollection<HeadlineDisplayItems> HeadlineCollection {         get; set; }
}

视图:

<StackPanel Orientation="Vertical" Margin="5,150 5 50" Name="HeadlinePanel">
    <TextBlock Text="Filtered Headlines From Monitoring List" 
   HorizontalAlignment="Left" Margin="0,0 5 5" Name="ScrollingHeadlineLabel" FontWeight="Bold" FontSize="14" Background="LightSkyBlue" />
    <ListBox ItemsSource="{Binding HeadlineCollection}">     
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Image Source="{Binding HeadlineIconPath}" />
                    <TextBlock><Run Text="{Binding Text}"/></TextBlock>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>                      
    </ListBox>
</StackPanel>

缺少的代码是对视图执行this.DataContext = new HeadlineViewModel();的位置。

编辑:如果您尝试从不同于视图线程的线程更新observableCollection,那么跨线程操作可能会遇到一些问题。解决方法是在这个链接中使用解决方案,但我认为这不是最好的方法。

将ObservableCollection创建为可以在XAML中引用的属性。直接在MainWindow类中创建它,或者将集合实例化为StaticResource。

将您的ObservableCollection作为ItemsSource绑定到您的Listbox

<ListBox ItemsSource="{Binding Path=HeadlineCollection}"></ListBox>

并使用DataTemplate将您的数据绑定到

<ListBox.ItemTemplate>
    <DataTemplate>
        <StackPanel Orientation="Horizontal">
            <Image ... />
            <TextBlock Text="{Binding Path=Text}" />
        </StackPanel>
    </DataTemplate>
</ListBox.ItemTemplate>

对于标题,创建一个数据类来管理您需要显示的内容(标题、图标等)

class Headline
{
    bool isTwitter {get; set;}
    string Text {get; set;}
}

然后,在您的客户端对象中,您可以通过调用add()-方法向ObservableCollection添加一个新对象,应用程序将自动呈现该新对象。

您可以在主UI线程上启动查询客户端,但对于响应UI,您应该让查询例程在自己的线程中运行(例如,使用BackgroundWorker),这样UI就不会被它弄得一团糟

最新更新