user1935987
user1935987

Reputation: 3347

How to bind textbox object to ViewModel

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}"/>

Upvotes: 1

Views: 3708

Answers (2)

FatemehEbrahimiNik
FatemehEbrahimiNik

Reputation: 613

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.

Upvotes: 0

Bart
Bart

Reputation: 10015

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; }
    }
}

Upvotes: 4

Related Questions