1

Trying to make my first application with the simple logging function to the TextBox on main form.

To implement logging, I need to get the TextBox object into the logger's class.

Prob - can't do that :) currently have no error, but as I understand the text value of TextBox is binding to my ViewModel, because getting 'null reference' exception trying to execute.

Logger.cs

public class Logger : TextWriter
{
        TextBox textBox = ViewModel.LogBox;
        public override void Write(char value)
        {
            base.Write(value);
            textBox.Dispatcher.BeginInvoke(new Action(() =>
            {
                textBox.AppendText(value.ToString());
            }));
        }

        public override Encoding Encoding
        {
            get { return System.Text.Encoding.UTF8; }
        }
}

ViewModel.cs

public class ViewModel
{
    public int ThreadCount { get; set; }
    public int ProxyTimeout { get; set; }

    public static TextBox LogBox { get; set; }
    //private TextBox _LogBox;
    //public TextBox LogBox {
    //    get { return _LogBox; }
    //    set {
    //        _LogBox = value;
    //    }
    //}
}

launching on btn click, MainWindow.xaml.cs:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        Logger logger = new Logger();
        logger.Write("ewgewgweg");
    }
}

MainWindow.xaml

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:tools"
        xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" x:Class="tools.MainWindow"
        mc:Ignorable="d"
        Title="Tools" Height="399.387" Width="575.46">

        <TextBox x:Name="logBox" 
            ScrollViewer.HorizontalScrollBarVisibility="Auto"
            ScrollViewer.VerticalScrollBarVisibility="Auto" HorizontalAlignment="Left" Height="137" Margin="10,222,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="394" Text="{Binding Path = LogBox, Mode=TwoWay}"/>

2 Answers 2

4

You have several issues in your code:

  • Don't bring controls (TextBox) in your viewmodel, if you do there's no use in trying to do MVVM.
  • The Text property in XAML has to be of the type String or something that can be converted to a string. You're binding a control, which will result in showing System.Windows.Controls.TextBox (result of .ToString()) on your screen instead of actual text.
  • Your LogBox property should implement INotifyPropertyChanged
  • You don't want TwoWay binding, as the text flows from your logger to the UI, you don't need it to flow back. You might even consider using a TextBlock instead or make the control readonly so people can't change the content.
  • You don't want static properties or static viewmodels, read up on dependency injection on how to pass dependencies.
  • You will be flooding your UI thread by appending your characters one by one. Consider using another implementation (but I won't go deeper into this for this answer).

Keeping all above in mind, I transformed your code to this.

MainWindow.xaml

    <TextBox x:Name="logBox" 
             HorizontalAlignment="Left" VerticalAlignment="Top" Height="137" Margin="10,222,0,0" 
             TextWrapping="Wrap"  Width="394" Text="{Binding Path = LogBox}"/>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    private Logger _logger;

    public MainWindow()
    {
        InitializeComponent();
        var viewModel = new ViewModel();
        DataContext = viewModel;
        _logger = new Logger(viewModel); // passing ViewModel through Dependency Injection
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        _logger.Write("ewgewgweg");
    }
}

ViewModel.cs

public class ViewModel : INotifyPropertyChanged
{
    public int ThreadCount { get; set; }
    public int ProxyTimeout { get; set; }

    private string _logBox;
    public string LogBox
    {
        get { return _logBox; }
        set
        {
            _logBox = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Logger.cs

public class Logger : TextWriter
{
    private readonly ViewModel _viewModel;

    public Logger(ViewModel viewModel)
    {
        _viewModel = viewModel;
    }

    public override void Write(char value)
    {
        base.Write(value);
        _viewModel.LogBox += value;
    }

    public override Encoding Encoding
    {
        get { return System.Text.Encoding.UTF8; }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

thank you very much for your answer. Yes, if i using just a string values of textbox this method isn't very good. i'm always used 'textBox.AppendText("text");' for logging, but worked only with WinForms before. Actually, i need to use logger in other different classes, not in the MainWindow.cs. DO you think your implementation is ok for that?
The code in my reply will work, but it can always be improved of course. I suggest you read up on dependency injection (link above) and MVVM Messaging and you're on the good path for a nice loosely coupled implementation.
0

You can use string instead of TextBox as follow as

In view model class

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private string _logBox;

    public string LogBox 
    { 
        get {return _logBox;}
        set
        {
            if(value != _logBox)
            {
                _logBox=value;
                OnPropertyChanged("LogBox");
            }
        }
    }

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

and in writer method you just

public void writer (string str)
{
    ViewModel.LogBox = str;
}

You can define ViewModel as static or create new object from ViewModel and access the object in logger class as you want!

hope this helped.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.