tags:

views:

148

answers:

2

Total beginner in WPF so bear with me.

I building a proof of concept application before with go ahead with it.I have decided to give it a go a WPF.

This is the scenario:

I have:

  • a View=CustomerView (userControl with a button "Buy Products")
  • a txtbox (append all the actions that occurs)
  • ViewModel=CustomerViewModel (talks to the service etc.. and gets results
  • Model =Customer (simple class with properties with InotifyPropertyChanged implemented

Given that I have a collection of products and for each product that I buy I need to APPEND TEXT TO the txtBox in the userControl printing all the actions that occurs, the textBox should look like

Start buying .....
Product 1 Sold
Product 2 Sold
Fineshed....

My problem is that despite I notify successfully after each item is sold I cannot seem to see how I can bind or make "Product 1 Sold,Product 2 appear in the textbox.

In windows forms I would have a userControl with a property called ProductStatus and whenever notified i would appendText to the textBox. "I put beginInvoke as I was getting threadCross operation" when coming from a service.That is another problem that I will have to investigate

private ProductStatus _productStatus;
public ProductStatus Status
{
  get { return _productStatus; }
  set
  {
    _printStatus = value;                 
    BeginInvoke((MethodInvoker)(() => txtProductResult.AppendText(string.Format("Product Sold {0}", Environment.NewLine))));  
  }
}

How do I append text to myTextBox for each item I sell?

=================Added current code===============================

    void LogChanged(object sender, NotifyCollectionChangedEventArgs e) 
        { 
            foreach(object item in e.NewItems) 
            { 
                txtProduct.AppendText(item.ToString()); 
            } 
        } 

         <TextBox Margin="12,41,12,59" Name="txtPrintResult" />
       <TextBox Text="{Binding TicketPrintLog, Mode=OneWay}" Margin="12,41,12,12" />


            ProductModel
            =============
             public string ProductLog{ get; set; }


            ProductViewModel
            ==================
            public string ProductLog
            {
                get { return _ProductModel.ProductLog; }
            }   
            internal void AppendToProductLogText(string text)
            {
                _ProductModel.ProductLog += text + Environment.NewLine;
                OnPropertyChanged("ProductLog");
            } 

            void ProductSoldNotificationReceived(object sender, notificationEventArgs e)
            {
                //THIS FIRES WHENEVER I PRODUCT IS SOLD.
                AppendToProductLogText(e.Message);
             }

            ProductView (userControl) XAML
            ================================
            <TextBox Margin="12,41,12,59" Name="txtResult" Text="{Binding ProductLog, Mode=OneWay}" />  

            IN CODE BEHIND I DO

              public void Action1()
              {
                txtResult.AppendText(_productViewModel.ProductLog);
              }
               public void SellProductAction()
              {
                txtResult.AppendText(_productViewModel.ProductLog);
              }

The problem i have is that i still have todo this

        txtResult.AppendText(_productViewModel.ProductLog); 

which defeates the point on databinding also when executing SellproductAction I only get notified about the last item ,It doesnt append Product1 sold ,Product 2 sold etc....

Any ideas

+3  A: 

First off, you can still do this the Windows Forms way, by appending the new text to the existing text:

txtProductResult = txtProductResult + Environment.NewLine + "Product sold";

(If you do this from a background thread you will need to use txtProductResult.Dispatcher.BeginInvoke instead of txtProductResult.BeginInvoke.)

However, the idiomatic way to do this in WPF is with data binding. So you would create a ProductSalesLog property on your view model, and bind the TextBox to that property. Then, whenever you update the ProductSalesLog property in the view model, the TextBox will update automatically. Note that your view model must implement INotifyPropertyChanged and must raise the PropertyChanged event for the ProductSalesLog property. Thus:

public class CustomerViewModel : INotifyPropertyChanged
{
  private string _productSalesLog = String.Empty;
  public string ProductSalesLog
  {
    get { return _productSalesLog; }
  }

  internal void AppendSalesLogText(string text)
  {
    _productSalesLog += text + Environment.NewLine;
    OnPropertyChanged("ProductSalesLog");
  }
}

And in your view:

<TextBox Text="{Binding ProductSalesLog, Mode=OneWay}" />
itowlson
I am digesting it.Coming back soon. I was thinking of having a collection and add to the collection whenever something changes but not sure how to implement it in wpf void ProductSalesLogChanged(object sender, notifyPropertyChangedEventArgs e) { foreach(object item in e.NewItems) { txtProduct.AppendText(item.ToString()); } }
For that approach you would want to make ProductSalesLog an `ObservableCollection<string>` and subscribe to its CollectionChanged event. But if you're doing that, don't make the UI element a TextBox: make it an ItemsControl and bind ItemsControl.ItemsSource to the collection; then WPF will update the UI for you without you needing to handle the event. **However**, WPF does *not* handle thread marshalling for collection changes, so you must take responsibility for updating the collection only from the UI thread.
itowlson
Thanks.Probably try then your simpler approach.Trying to make it work.Let you know
Hi,Not sure what I am doing wrong but now the text box is empty all the time.In my userControl ProductView i have added this <TextBox Text="{Binding ProductSalesLog, Mode=OneWay}" Margin="12,41,12,12" /> Then in my ProductViewModel I have added internal void AppendToTicketLogText(string text) { _ProductSalesModel.ProductSaleLog += text + Environment.NewLine; OnPropertyChanged("ProductSaleLog "); } Basically the ProductViewModel updates a property on the ProductModel.Not sure What I am doing wrong.
What's your DataContext? Are you getting any binding errors in the Output window?
itowlson
A: 

As WPF is a data driven framework, you should have your view databinding to properties on your view model and model. Any changes in the viewmodel or model can then be passed to the view through the INotifyPropertyChanged interface (by raising a propertyChanged event).

This means for most purposes you should not need to access properties on your user control. This should be possible by having a product status on your viewmodel and your text box should bind to this property. You will of course have to make sure when the setter for product status is called that the PropertyChanged event is raised to notify the view to update

mattythomas2000
Hi,thanks for your reply.I do have all the propertyChanged firing and so forth but Cannot seem to be able to implement the final bit,such us update the silly textbox .Trying to figure it out
Would you be able to post the code that illustrates your binding to the property in xaml and the code that shows the event being raised. Maybe something is slightly wrong along there as the text box should update when the bindings and property notifications are in place
mattythomas2000
ammeded post with some code .For some reason i could not update in comment.so see original post I still have problems .hope all clear
Are you updating your viewmodel from a separate thread. If this is the case you may need to be careful about how you raise your change notification, i.e. checking the dispatcher has access and invoking onto the ui thread if it doesn't. This post shows how to do this -> http://www.ingebrigtsen.info/post/2010/01/16/Dispatcher-Safe-INotifyPropertyChanged.aspx
mattythomas2000