Function call works when called internally from in its control but not externally from the main app

StackOverflow https://stackoverflow.com/questions/20085272

  •  02-08-2022
  •  | 
  •  

Question

I have used some code that i retrieved from this website to create a scrolling terminal type window. The example works very well, so i decided i would turn it into a basic control so i could reuse. In the control I have a "LogEntry" type list that is databound to an "items control" so when the list gets updated, so does the items control. This list gets updated by calling the "AddEntry" function. When this function is called internally from the control, the list notifies the "ItemsControl" that an update has occured. When I call the "AddEntry()" function from the main window, the list updates BUT the "ItemsControl" doesnt get the update notification. Can anyone explain why? Thanks for your help Regards Stuart

here is the code for the user control

TerminalWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace ScrollingTerminalWindow
{
    /// <summary>
    /// example code retreived from this page
    /// http://stackoverflow.com/questions/16743804/implementing-a-log-viewer-with-wpf
    /// </summary>
    public partial class TerminalWindow : UserControl
    {
            private int index;
            private string TestData = "Lorem ipsum dolor sit amet, consectetur     adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum";
            private List<string> words;
            private int maxword;

            public ObservableCollection<LogEntry> LogEntries { get; set; }

            public TerminalWindow()
            {
                InitializeComponent();
                index = 0;
                DataContext= LogEntries = new ObservableCollection<LogEntry>();

                random = new Random();
                words = TestData.Split(' ').ToList();
                maxword = words.Count - 1;
                AddEntry();
                this.btnAddEntry.BringIntoView();
                this.btnAddEntry.Focus();

            }



             public void AddEntry()
            {
                Dispatcher.BeginInvoke((Action)(() =>     LogEntries.Add(GetRandomEntry())));
            }
             private System.Threading.Timer Timer;
             private System.Random random;
             private LogEntry GetRandomEntry()
             {
             if (random.Next(1, 10) > 1)
             {
                 return new LogEntry()
                 {
                     Index = index++,
                     DateTime = DateTime.Now,
                     Message = string.Join(" ", Enumerable.Range(5, random.Next(10, 50))
                                                          .Select(x => words[random.Next(0, maxword)])),
                 };
             }

             return new CollapsibleLogEntry()
             {
                 Index = index++,
                 DateTime = DateTime.Now,
                 Message = string.Join(" ", Enumerable.Range(5, random.Next(10, 50))
                                              .Select(x => words[random.Next(0, maxword)])),
                 Contents = Enumerable.Range(5, random.Next(5, 10))
                                      .Select(i => GetRandomEntry())
                                      .ToList()
             };

         }

         private void btnAddEntry_Click(object sender, RoutedEventArgs e)
         {
             AddEntry();
         }



    }
    public class LogEntry : PropertyChangedBase
    {
        public DateTime DateTime { get; set; }

        public int Index { get; set; }

        public string Message { get; set; }
    }

    public class CollapsibleLogEntry : LogEntry
    {
        public List<LogEntry> Contents { get; set; }
    }
    public class PropertyChangedBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            Application.Current.Dispatcher.BeginInvoke((Action)(() =>
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
            }));
        }
    }

}

TerminalWindow.XAML

<UserControl x:Class="ScrollingTerminalWindow.TerminalWindow"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:ScrollingTerminalWindow"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
    <Style TargetType="ItemsControl" x:Key="LogViewerStyle">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <ScrollViewer CanContentScroll="True">
                        <ItemsPresenter/>
                    </ScrollViewer>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel IsItemsHost="True"/>
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <DataTemplate DataType="{x:Type local:LogEntry}">
        <Grid IsSharedSizeScope="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="Index" Width="Auto"/>
                <ColumnDefinition SharedSizeGroup="Date" Width="Auto"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TextBlock Text="{Binding DateTime}" Grid.Column="0"
                   FontWeight="Bold" Margin="5,0,5,0"/>

            <TextBlock Text="{Binding Index}" Grid.Column="1"
                   FontWeight="Bold" Margin="0,0,2,0" />

            <TextBlock Text="{Binding Message}" Grid.Column="2"
                   TextWrapping="Wrap"/>
        </Grid>
    </DataTemplate>

    <DataTemplate DataType="{x:Type local:CollapsibleLogEntry}">
        <Grid IsSharedSizeScope="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="Index" Width="Auto"/>
                <ColumnDefinition SharedSizeGroup="Date" Width="Auto"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>

            <TextBlock Text="{Binding DateTime}" Grid.Column="0"
                   FontWeight="Bold" Margin="5,0,5,0"/>

            <TextBlock Text="{Binding Index}" Grid.Column="1"
                   FontWeight="Bold" Margin="0,0,2,0" />

            <TextBlock Text="{Binding Message}" Grid.Column="2"
                   TextWrapping="Wrap"/>

            <ToggleButton x:Name="Expander" Grid.Row="1" Grid.Column="0"
                      VerticalAlignment="Top" Content="+" HorizontalAlignment="Right"/>

            <ItemsControl ItemsSource="{Binding Contents}" Style="{StaticResource LogViewerStyle}"
                      Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"
                      x:Name="Contents" Visibility="Collapsed"/>

        </Grid>
        <DataTemplate.Triggers>
            <Trigger SourceName="Expander" Property="IsChecked" Value="True">
                <Setter TargetName="Contents" Property="Visibility" Value="Visible"/>
                <Setter TargetName="Expander" Property="Content" Value="-"/>
            </Trigger>
        </DataTemplate.Triggers>
    </DataTemplate>

</UserControl.Resources>



<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="60"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <Button Name="btnAddEntry" Click="btnAddEntry_Click" Width="80" Height="40" HorizontalAlignment="Left" VerticalAlignment="Top" Content="Add Entry" Grid.Row="0">

    </Button>
<DockPanel Grid.Row="1">
                <TextBlock Text="{Binding Count, StringFormat='{}{0} Items'}" 
           DockPanel.Dock="Top"/>
        <ItemsControl ItemsSource="{Binding}" Style="{StaticResource LogViewerStyle}" DataContext="{Binding}">
            <ItemsControl.Template>
                        <ControlTemplate>
                            <ScrollViewer CanContentScroll="True">
                                <ItemsPresenter/>
                            </ScrollViewer>
                        </ControlTemplate>
                    </ItemsControl.Template>
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <VirtualizingStackPanel IsItemsHost="True"/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </DockPanel>

</Grid>

Here is the code for the main window ap that tests the control

mainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ScrollingTerminalWindow;

namespace ScrollingTextBoxTestApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
public partial class MainWindow : Window
{
    TerminalWindow tw;
    public MainWindow()
    {
        InitializeComponent();
        tw = new TerminalWindow();
    }

    private void btn_Click(object sender, RoutedEventArgs e)
    {
        tw.AddEntry();
    }
}

}

mainwindow.xaml

<Window x:Class="ScrollingTextBoxTestApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ScrollingTerminalWindow;assembly=ScrollingTerminalWindow"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="60"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>

    </Grid.RowDefinitions>
    <Button Name="btn" Click="btn_Click" Grid.Row="0" Content="MainPageAddEntry"></Button>
    <local:TerminalWindow Margin="10,10,0,0" Name="terminalWindow1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Row="1"/>
</Grid>

Was it helpful?

Solution

Try to replace this line:

tw = new TerminalWindow();

with this one:

tw = terminalWindow1;

You created another TerminalWindow object apart from the one you defined in XAML. You need to reference the one you defined in XAML then invoke its AddEntry() instead, to see the result.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top