MVVM WPF BackgroundWorker and UI update - c#

i'm learning MVVM pattern with wpf and i'm trying to create a simple splashscreen for loading applications.
I have a simple class called Loading with two property which are bounded to my interface.
public class Loading : INotifyPropertyChanged
{
/// <summary>
/// Define current status value from 0 to 100.
/// </summary>
private int _currentStatus;
/// <summary>
/// Define current status text.
/// </summary>
private string _textStatus;
/// <summary>
/// Define constructor.
/// </summary>
public Loading(int status, string statusText)
{
_currentStatus = status;
_textStatus = statusText;
}
public int CurrentStatus
{
get { return _currentStatus; }
set
{
_currentStatus = value;
OnPropertyChanged("CurrentStatus");
}
}
public string TextStatus
{
get { return _textStatus; }
set
{
_textStatus = value;
OnPropertyChanged("TextStatus");
}
}
#region Interfaces
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
From my ctor ViewModel i instanciate this model
Loading = new Loading(0, "Loading...");
and run a new thread calling the function GetSystemInfo() which perform some stuff in order to load some information.
Thread systemInfo = new Thread(GetSystemInfo);
systemInfo.IsBackground = true;
systemInfo.Start();
I'm updating the ui from GetSystemInfo() with
Loading.TextStatus = "Loading User Information...";
Loading.CurrentStatus = 50;
So far so good.. the thread is correctly updating the ui but the problem is that i wish to close this splashcreen and open a new window when the loading is complete but i'm unable to check if the thread is complete or at least i don't found a way to do that.
Is there any way i can solve this problem?
Thanks.

You achieve this rather easily by using the Task class (via Task Parallel Library) with a combination of async-await.
What happens when you await on a Task is that the control is yielded back to the caller. In your case, the caller comes from the UI thread, so the control will return to the UI message loop, keeping you app responsive. Once the thread finishes it's work, it will return to the next line after the await, where you can then open the splash screen.
It will look like this:
public async void MyEventHandler(object sender, EventArgs e)
{
await Task.Run(() => GetSystemInfo());
// Here, you're back on the UI thread.
// You can open a splash screen.
}

Related

INotifyPropertyChanged implemented, but textblock only updates once

ANSWERED BY #grek40
I have implemented INotifyPropertyChanged in my project and for everything else it is working fine, but for this one variable-bound textblock it is only updating once on the main window load event.
I'm probably just missing some little detail somewhere, please help!
MainWindow.xaml
<TextBlock HorizontalAlignment="Left" Margin="317,161,0,0"
TextWrapping="Wrap" Text="{Binding ish.IsDoingWork, Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top"/>
<Button Command="{Binding hksVm.HentKundeStatus}" Content="Invoke"
HorizontalAlignment="Left" Margin="704,139,0,0" VerticalAlignment="Top"
Width="75"/>
MainWindow.xaml.cs (setting data context)
public MainWindow()
{
if (!ValidationHandler.GrantAccess().Equals(3))
{
InitializeComponent();
DataContext = new
{
hksVm = new HentKundeStatusVm(),
ish = new InvocationServiceHandler()
};
}
else
{
Close();
}
}
ViewModel.cs
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Input;
namespace MyNamespace
{
public class HentKundeStatusVm : IViewModel
{
private ICommand _hentKundeStatus;
private readonly InvocationServiceHandler _invocationServiceHandler = new InvocationServiceHandler();
public ICommand HentKundeStatus => HentKundeStatusCommand();
public ICommand HentKundeStatusCommand()
{
if (ValidationHandler.GrantAccess() < 2)
{
return _hentKundeStatus ?? (_hentKundeStatus = new RelayCommand(param =>
ElapsedTime = _invocationServiceHandler.ExecuteAndTimeAction(
() =>
{
//web API kaldes asynkront - husk: using System.Net.Http;
using (var client = new HttpClient().GetAsync("API-url"))
{
client.Result.Content.ReadAsStringAsync();
}
}, AntalKald)));
}
return null;
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
InvocationServiceHandler.cs
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading.Tasks;
using App005_WebServiceTestingTool_Domain.Annotations;
namespace App005_WebServiceTestingTool_Domain.Handlers
{
public class InvocationServiceHandler : INotifyPropertyChanged
{
// This works on the main_window load event and set the textblock in the view
private string _isDoingWork = "Currently not working";
public string IsDoingWork
{
get => _isDoingWork;
set
{
_isDoingWork = value;
NotifyPropertyChanged(nameof(IsDoingWork));
}
}
/// <summary>
/// Method that invokes action parameter x times in multiple threads (parallel) and returns the elapsed time
/// </summary>
/// <param name="action"></param>
/// <param name="antalKald"></param>
/// <returns></returns>
public string ExecuteAndTimeAction(Action action, string antalKald)
{
// Here is set the bound variable, and if I debug I can see it getting set to Working...
IsDoingWork = "Working...";
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < Convert.ToInt32(antalKald); i++)
{
action.Invoke();
}
sw.Stop();
// Here I am resetting the variable and again in debug I can see it change, but nothing happens in the view
IsDoingWork = "";
return $"Elapsed time: {sw.Elapsed}";
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
public void NotifyPropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
Basically, you have two instances of InvocationServiceHandler. One inside the DataContext as ish = new InvocationServiceHandler() and the other inside MyViewModel as private readonly InvocationServiceHandler _invocationServiceHandler = new InvocationServiceHandler();
So ish.IsDoingWork is displayed and something like hksVm._invocationServiceHandler.IsDoingWork is updated. It's not really clear since HentKundeStatusVm and MyViewModel are not really the same thing in the question.
It should be possible to fix this situation by Constructor Injection of the service handler:
public class HentKundeStatusVm : IViewModel
{
private readonly InvocationServiceHandler _invocationServiceHandler;
public HentKundeStatusVm(InvocationServiceHandler ish)
{
_invocationServiceHandler = ish;
}
// the other stuff
}
Then
// In the MainWindow constructor
var ishInstance = new InvocationServiceHandler();
DataContext = new
{
hksVm = new HentKundeStatusVm(ishInstance),
ish = ishInstance
};
Then you have the same handler instance available for binding and for execution.
Your logic is fine , everything looks properly attached and the values are updated as expected . The only problem is your UI isn't updating it because you're executing the For loop on the main thread which unfortunately is blocking all your UI updates.
So You can
Run ExecuteAndTimeAction as a background operation using, Backgroundworker/[Task Library][1].
2.Use Dispatcher to flush your UI messages, eg:
/// <summary>
/// Enters the message loop to process all pending messages down to the specified
/// priority. This method returns after all messages have been processed.
/// </summary>
/// <param name="priority">Minimum priority of the messages to process.</param>
public static void DoEvents(DispatcherPriority priority = DispatcherPriority.Background)
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(
priority,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
private static object ExitFrame(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
And Call
/// <summary>
/// Method that invokes action parameter x times in multiple threads (parallel) and returns the elapsed time
/// </summary>
/// <param name="action"></param>
/// <param name="antalKald"></param>
/// <returns></returns>
public string ExecuteAndTimeAction(Action action, string antalKald)
{
// Here is set the bound variable, and if I debug I can see it getting set to Working...
IsDoingWork = "Working...";
DoEvent();//flushes the UI msg queue
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < Convert.ToInt32(antalKald); i++)
{
action.Invoke();
}
sw.Stop();
// Here I am resetting the variable and again in debug I can see it change, but nothing happens in the view
IsDoingWork = "";
DoEvent();//flushes the UI msg queue
return $"Elapsed time: {sw.Elapsed}";
}
The second approach is a hack and it will still freeze
the UI but will get the job done for you.
I suggest you go for the first approach,
It is way better but takes effort to implement.

How to stop Execution of Thread from other thread?

I have designed MMVM pattern in C#. My GUI has different button. Each button is for particular command. These commands are derived from CommandsBase Class. Each command runs on seperate thread by calling CommandExecute. There are several commands like CommandRunMode1, CommandRunMode2, commandDiagnosys etc. Now new requirement has been arised to abort command. I am trying to write CommandAbort Class. And the problem is how to abort already executing command when ABORT button is pressed on GUI (i.e. stop other thread in the halfway from CommandAbort class thread).
enter code here
#region COMMAND_Base
public abstract class CommandsBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected delegate void NoArgsDelegate();
protected delegate void OneArgDelegate(string arg1);
protected delegate void TwoArgDelegate(string arg1, bool arg2);
protected ViewModelBase ParentViewModel;
private StatusIndicator _isEnableState;
string _uiText;
bool _uiEnable;
RelayCommand _command;
protected Dispatcher _dispatcher;
private readonly int _responseDelay = 2000; // milliseconds
#region PROPERTIES
public CommandText CommandText { get; set; } // Ui Text
public CommandStatusIndicator CommandStatus { get; set; } // background color
public StatusIndicator IsEnableState
{
get { return _isEnableState; }
set
{
_isEnableState = value;
OnChanged("Status");
}
}
public string UiText
{
get { return _uiText; }
set
{
_uiText = value;
OnChanged("UiText");
}
}
public bool UiEnabled
{
get
{
return _uiEnable;
}
set
{
_uiEnable = value;
OnChanged("UiEnabled");
}
}
public ICommand Command
{
get
{
if (_command == null)
{
_command = new RelayCommand(param => this.CommandExecute(), param => this.CommandCanExecute);
}
return _command;
}
}
public int NumberOfAttempts;
public int ResponseDelay
{
get
{
return _responseDelay;
}
}
#endregion
protected CommandsBase()
{
}
protected void UpdateUi(string text)
{
UiText = text;
}
protected void UpdateUi(bool enabled)
{
UiEnabled = enabled;
}
protected void UpdateUi(string text, bool enabled)
{
UiText = text;
UiEnabled = enabled;
}
#region COMMAND_EXECUTION
public virtual void CommandExecute()
{
NoArgsDelegate commandExecution = new NoArgsDelegate(CommandExecuteInAThread);
commandExecution.BeginInvoke(null, null);
}
protected abstract void CommandExecuteInAThread();
public abstract bool CommandCanExecute { get; }
#endregion
public virtual void OnChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
#endregion
region COMMAND_RunMode1
public class CommandRunMode1 : CommandsBase
{
public CommandRunMode1(string uiText, bool isEnabled, ViewModelBase parentViewModel)
{
UiEnabled = isEnabled;
ParentViewModel = parentViewModel;
_dispatcher = Dispatcher.CurrentDispatcher;
UiText = uiText;
IsEnableState = new StatusIndicator(null, null);
}
#region COMMAND_EXECUTION
public override bool CommandCanExecute
{
get
{
return true;
}
}
/// <summary>
/// This is a method run asynchronously, so that executing a command might not stop the UI events
/// </summary>
protected override void CommandExecuteInAThread()
{
// Transmit command to Mode1
ApplicationLayer.TransmitString("START1");
while (Display.CurrentScreent != DisplayController.CurrentScreen.ST1SCREEN);
ApplicationLayer.TransmitString("MODE1ENTER");
while (Display.CurrentScreent != DisplayController.CurrentScreen.MD1SCREEN);
ApplicationLayer.TransmitString("PROCESSCREEN");
while (Display.CurrentScreent != DisplayController.CurrentScreen.PROCESSCREEN);
}
#endregion
}
endregion
region COMMAND_RunMode2
public class CommandRunMode2 : CommandsBase
{
public CommandRunMode2(string uiText, bool isEnabled, ViewModelBase parentViewModel)
{
UiEnabled = isEnabled;
ParentViewModel = parentViewModel;
_dispatcher = Dispatcher.CurrentDispatcher;
UiText = uiText;
IsEnableState = new StatusIndicator(null, null);
}
#region COMMAND_EXECUTION
public override bool CommandCanExecute
{
get
{
return true;
}
}
/// <summary>
/// This is a method run asynchronously, so that executing a command might not stop the UI events
/// </summary>
protected override void CommandExecuteInAThread()
{
// Transmit command to Mode2
ApplicationLayer.TransmitString("START2");
while (Display.CurrentScreent != DisplayController.CurrentScreen.ST2SCREEN);
ApplicationLayer.TransmitString("MODE2ENTER");
while (Display.CurrentScreent != DisplayController.CurrentScreen.MD2SCREEN);
ApplicationLayer.TransmitString("PROCESSCREEN");
while (Display.CurrentScreent != DisplayController.CurrentScreen.PROCESSCREEN);
}
#endregion
}
endregion
region COMMAND_Abort
public class CommandAbort : CommandsBase
{
public CommandAbort (string uiText, bool isEnabled, ViewModelBase parentViewModel)
{
UiEnabled = isEnabled;
ParentViewModel = parentViewModel;
_dispatcher = Dispatcher.CurrentDispatcher;
UiText = uiText;
IsEnableState = new StatusIndicator(null, null);
}
#region COMMAND_EXECUTION
public override bool CommandCanExecute
{
get
{
return true;
}
}
/// <summary>
/// This is a method run asynchronously, so that executing a command might not stop the UI events
/// </summary>
protected override void CommandExecuteInAThread()
{
// Transmit command to Abort currently running command
ApplicationLayer.TransmitString("ABRT");
}
#endregion
}
endregion
Cancellation of a thread should always be cooperative.
What do i mean by that?
The code running in your thread should periodically check to see if it should continue. This may be a boolean somewhere. If cancellation is required, simply cleanup the resources you are using and return.
volatile bool IsCancelled = false;
void DoWork()
{
while(!IsCancelled)
{
//do work
}
}
When is this flag set?
Perhaps you have a Cancel button. Pushing this would trigger an event handler. This event handler sets the flag. The next time your threaded code checks the flag, it will cancel the operation. This is why it is called cooperative cancellation. 2 threads work together to make it happen.
Im afraid you have an architectural challenge to overcome
In MVVM, commands are the pipeline in which user interaction is communicated to the view model. The View model should react to this command by 'doing to the work' by calling the appropriate methods on other classes ( your model/foundation layer/business objects...use whichever words you prefer). You will need an implementation of DelegateCommand or RelayCommand.
User clicks Button
Button executes command
Command invokes method on view model
View model calls your domain classes to do the work
Why structure it this way? Now you can have your cancellation flag in the view model. This is one of the things a view model is for - storing state!
If you are able to, I recommend using .Net's Task Parallel Library. It supports cooperative cancellation for free!
To sum up. I strongly recommend you move the serial code out of the command classes. It is possible to make it work with your design but this is not good practice :(

Attach event to a class

I'm trying to register an event to the MainPage.xaml.cs like this :
public partial class MainPage : PhoneApplicationPage
{
public static ICanvas CurrentCanvas;
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
//HERE I TRY TO REGISTER FOR MY EVENT
((WP8Canvas)CurrentCanvas).Redraw += WP8EventHandler_RedrawCanvas;
//HERE I TRY TO REGISTER FOR MY EVENT/
System.Threading.ThreadStart start = new System.Threading.ThreadStart(launchProcessA);
System.Threading.Thread t = new System.Threading.Thread(start);
t.Name = "ProcessA Thread";
t.Start();
}
/// <summary>
/// Listen to WP8Canvas repaint() methods
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void WP8EventHandler_RedrawCanvas(object sender, EventArgs e)
{
wpCanvas.InvalidateArrange();
Debug.WriteLine("Redraw Canvas Event");
}
}
WP8Canvas.cs
public class WP8Canvas : WP8Displayable, ICanvas
{
public Canvas canvas ;
public event EventHandler Redraw;
protected object reference;
public WP8Canvas(object reference)
{
this.reference = reference;
MainPage.CurrentCanvas = this;
Debug.WriteLine("WP8Canvas instance");
}
public void repaint()
{
RedrawCanvas();
}
/// <summary>
/// Raise event if repaint() method is hit
/// </summary
private void RedrawCanvas()
{
//Null check makes sure the main page is attached to the event
if (this.Redraw != null)
this.Redraw(new object(), new EventArgs());
}
}
Between my comment //HERE I TRY TO REGISTER FOR MY EVENT I try to register an non-instantiated object WP8Canvas but of course a null exception occurs. It's my Thread t who controls the creation of WP8Canvas object but this happens during run-time and I don't know when.
QUESTION : How can I register my WP8Canvas Redraw event to the MainPage without facing that null pointer exception.
If you have no control over the launchProcessA, then a way to reach this result is to use a property:
public WP8Canvas CurrentWP8Canvas
{
get
{
return this.CurrentCanvas as WP8Canvas;
}
set
{
this.CurrentCanvas = value;
value.Redraw += WP8EventHandler_RedrawCanvas;
}
}
Then you juste have to change your code so that the thread assigns the CurrentWP8Canvas property instead of CurrentCanvas
Edit: If you need it to be static, one way could be to store the event handler in a temporary static variable:
public static EventHandler RedrawCanvas { get; set; }
Then set it from MainPage's constructor:
RedrawCanvas = WP8EventHandler_RedrawCanvas;
Finally, declare CurrentWP8Canvas as static, and assign the event handler you stored:
public static WP8Canvas CurrentWP8Canvas
{
get
{
return CurrentCanvas as WP8Canvas;
}
set
{
CurrentCanvas = value;
value.Redraw += RedrawCanvas;
}
}
It should work. However, it's terribly wrong, for many reasons: thread synchronization issues, risks of memory leaks... At that point, you're supposed to conclude that you've reached a dead-end, backtrack, and consider redesigning your architecture.

DataContext - ListView - Refresh UI - INotifyPropertyChanged

Working on an windows store app, I try to update/refresh a listView when some data is updated. But despite all samples and documentations I read, it doesn't work...
Here my code behind :
(I do not provide my xaml files, it's just a sample listView).
So the class which implements the INotifyPropertyChanged interface:
public class Download : INotifyPropertyChanged
{
public enum DownloadState
{
Running,
Waiting,
Pausing,
Paused,
Cancelling,
Cancelled
};
private String Uri;
private StorageFile storageFile;
private String tempFileName;
private String fileName;
private String version ;
private long totalSize ;
private long downloadedBytes;
private DownloadState state;
private Protocol protocol;
public Download(String Uri, StorageFile file, String fileName, String version, long totalSize, Protocol protocol)
{
this.Uri = Uri;
this.storageFile = file;
this.tempFileName = "";
this.fileName = fileName;
this.version = version;
this.totalSize = totalSize;
this.downloadedBytes = 0;
this.state = DownloadState.Waiting;
this.protocol = protocol;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
System.Diagnostics.Debug.WriteLine("Update!"); //ok
if (PropertyChanged != null)
{
//PropertyChanged is always null and shouldn't.
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public DownloadState State
{
get{return this.state;}
set {
this.state = value;
NotifyPropertyChanged();
}
}
//+some others methods
}
}
And the main page of the metro app :
// The Basic Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234237
namespace ClientAirNavLight_WS
{
/// <summary>
/// A basic page that provides characteristics common to most applications.
/// </summary>
public sealed partial class MainPage : ClientAirNavLight_WS.Common.LayoutAwarePage
{
/// <summary>
/// Represent a Web Service proxy.
/// </summary>
private AirNavLight_WSClientClient proxyWS;
/// <summary>
/// Initiialize the component of the application's main page.
/// </summary>
public MainPage()
{
this.InitializeComponent();
}
/// <summary>
/// Populates the page with content passed during navigation. Any saved state is also
/// provided when recreating a page from a prior session.
/// </summary>
/// <param name="navigationParameter">The parameter value passed to
/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
/// </param>
/// <param name="pageState">A dictionary of state preserved by this page during an earlier
/// session. This will be null the first time a page is visited.</param>
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
}
/// <summary>
/// Preserves state associated with this page in case the application is suspended or the
/// page is discarded from the navigation cache. Values must conform to the serialization
/// requirements of <see cref="SuspensionManager.SessionState"/>.
/// </summary>
/// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
protected override void SaveState(Dictionary<String, Object> pageState)
{
}
//Simulate data update.
private async void Button_Click(object sender, RoutedEventArgs e)
{
object selected = this.ListResult.SelectedItem;
if (selected != null)
{
//simulate an update in data.
// ListView should be refresh in order to reflect changes made here.
Download dl = (Download)selected;
if (dl.State == Download.DownloadState.Paused)
{
dl.State = Download.DownloadState.Running;
}
else
{
dl.State = Download.DownloadState.Paused;
}
}
else
{
//Just add an item to the list view.
StorageFile file = await this.getStorageFile("test");
Download dl = new Download("192.128.2.14", file, "test", "1.2", 100, Protocol.HTTP);
this.ListResult.Items.Add(dl);
this.ListResult.DataContext = dl;// Does not work.
}
}
private async Task<StorageFile> getStorageFile(String suggestedFileName)
{
FileSavePicker savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
// Dropdown of file types the user can save the file as
savePicker.FileTypeChoices.Add("Application/pdf", new List<string>() { ".pdf" });
savePicker.FileTypeChoices.Add("Archive", new List<string>() { ".zip", ".rar", ".7z" });
savePicker.FileTypeChoices.Add("Plain-text", new List<string>() { ".txt" });
// Default file name if the user does not type one in or select a file to replace
savePicker.SuggestedFileName = suggestedFileName;
return await savePicker.PickSaveFileAsync();
}
}
}
So How am I supposed to use listView.DataContext? Am I misunderstanding how use INotifyPropertyChanged?
EDIT :
How my listView is define :
<ListView x:Name="ListResult" HorizontalAlignment="Left" Height="200" Margin="10,56,0,0" VerticalAlignment="Top" Width="653" SelectionMode="Single"/>
Why are you setting this.ListResult.DataContext = dl? When dealing with ListViews, the ItemsSource is the collection for the all the objects that get iterated, not the DataContext. Furthermore, ItemsSource should be a collection and not one item. So if you're binding to a property, that property should exist as an item's property in the ItemsSource collection.
However, it looks like you don't have a collection so you're adding the dl directly to the ListView with this.ListResult.Items.Add(dl). That approach should work, however take out the this.ListResult.DataContext = dl or set this.ListResult.ItemsSource to a collection and add dl to that collection
I'm not familiar with [CallerMemberName] but if you're having problems with propertyName being null, try the following
public DownloadState State
{
get{return this.state;}
set {
if( this.state != value )
{
this.state = value;
NotifyPropertyChanged("State");
}
}
}
Furthermore, all properties that you want to bind to should be public and call NotifyPropertyChanged, like the following
private long totalSize ;
public long TotalSize
{
get{return this.totalSize;}
set {
if( this.totalSize != value )
{
this.totalSize = value;
NotifyPropertyChanged("TotalSize");
}
}
}
In your Download class, the only Property that will update in the UI is State, since it has a getter and setter.
Without your XAML it's not possible to see what you are trying to display, but normally, you would bind properties of your DataContext object (dl in this case) to different controls in a datatemplate that the listview could then display.
But these properties need to be public and have getters and setters for INotifyPropertyChanged to update the UI that these properties are bound to.
For instance, if you wanted the filename to show up in the ListView, you would need, as a minimum, to declare the filename property like this:
public String fileName {get; set; }
Zangdak -- One way around that problem is each time the items change in your collection just set set the ItemSource of the ListView to null and then set it back to your collection. If you do this the UI will update and you will see your new items added to your collection.
Paige Ake

C# Variable labels?

Say I have a box that says ENABLED or DISABLED.
How can I make the text vary depending on a state?
public void CheckBox1CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked) {
checkBox1.Text = "Enabled";
}
else {
checkBox1.Text = "Disabled";
}
}
box.Text = (box.Enabled ? "ENABLED" : "DISABLED");
If I understand correctly, you are asking how to have a label or some other bit of UI text automatically update to reflect a "state variable". This is just one way to accomplish what you're describing:
I would do it by having a central state object which implements INotifyPropertyChanging and INotifyPropertyChanged. When your application initializes, attach event handlers to the events those interfaces expose, and one of those event handlers can change the text of the label when property (Foo) changes.
public class State : INotifyPropertyChanging, INotifyPropertyChanged
{
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanging(PropertyChangingEventArgs e)
{
if (this.PropertyChanging != null)
{
this.PropertyChanging(this, e);
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, e);
}
}
public bool Foo
{
get
{
return foo;
}
set
{
if (value != foo)
{
this.OnPropertyChanging(new PropertyChangingEventArgs("Foo"));
foo = value;
this.OnPropertyChanged(new PropertyChangedEventArgs("Foo"));
}
}
}
private bool foo = false;
}
protected void HandleStateChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Foo")
{
box.Text = state.Foo ? "Enabled" : "Disabled";
}
}
What jeffamaphone said, but I should add that any time that state changes you will have to make sure to run that same code. The easiest way to insure this happens is by binding the box.Text property to the state object that you are interested in. That way, any change made to the object is immediately reflected in the text.
This blog post
helped me get started with data binding.... because I love FAQs.
The last few months I have been going with a slightly lighter weight solution than implementing a whole class to manage state. I usually define an enum which indicates the types of states available in the UI, then I have a function that makes changes to the UI, based on the state selected. This approach has been very successful, and not too heavy in terms of the amount of code needed to be written.
If I want to know what states are available in the UI, I can check the values of the enum.
public enum SystemState
{
/// <summary>
/// System is under the control of a remote logging application.
/// </summary>
RemoteMode,
/// <summary>
/// System is not under the control of a remote logging application.
/// </summary>
LocalMode
}
public interface IView
{
void SetState(SystemState state);
}
//method on the UI to modify UI
private void SetState(SystemState state)
{
switch (state)
{
case SystemState.LocalMode:
//for now, just unlock the ui
break;
case SystemState.RemoteMode:
//for now, just lock the ui
break;
default:
throw new Exception("Unknown State requested:" + state);
}
}
//now when you change state, you can take advantage of intellisense and compile time checking:
public void Connect()
{
SetState(SystemState.RemoteMode);
}

Categories

Resources