LetzerWille LetzerWille - 1 month ago 8
C# Question

Can't make timer update property

Drive usage info shows correctly. But when I switch off of the drives the disk usage info is not updated. I can't figure out why.

<local1:Drives x:Key="drivesUsage" />

<StackPanel
Name="stkDrives"
Grid.Row="3"
Grid.ColumnSpan="2"
DataContext="{StaticResource drivesUsage}"
Orientation="Horizontal">
<Label Margin="10" VerticalAlignment="Center">
C:
</Label>
<TextBox
Name="txbDriveC"
Margin="2"
VerticalAlignment="Center"
Style="{StaticResource txbStyle}"
Text="{Binding DriveC}" />
<Label Margin="15" VerticalAlignment="Center">
D:
</Label>
<TextBox
Name="txbDriveD"
Margin="2"
VerticalAlignment="Center"
Style="{StaticResource txbStyle}"
Text="{Binding DriveD}" />
<Label Margin="15" VerticalAlignment="Center">
I:
</Label>
<TextBox
Name="txbDriveI"
Margin="2"
VerticalAlignment="Center"
Style="{StaticResource txbStyle}"
Text="{Binding DriveI}" />
<Label Margin="15" VerticalAlignment="Center">
H:
</Label>
<TextBox
Name="txbDriveH"
Margin="2"
VerticalAlignment="Center"
Style="{StaticResource txbStyle}"
Text="{Binding DriveH}" />
</StackPanel>

public partial class MainWindow : Window
{
public MainWindow()
{


InitializeComponent();

Timer myTimer = new Timer(1000);
myTimer.Start();
myTimer.Elapsed += new ElapsedEventHandler(timeToCall);

}


private void timeToCall(object sender, ElapsedEventArgs e)
{
DrivesCheck();
}

private void DrivesCheck()
{
Drives d = new Drives();
}

}
public class Drives : INotifyPropertyChanged

{
public event PropertyChangedEventHandler PropertyChanged;

private void OnPropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}

private string driveC;
private string driveD;
private string driveI;
private string driveH;

public string DriveC
{
get { return driveC; }
set
{
driveC = value;
OnPropertyChanged(nameof(DriveC));
}
}

public string DriveD
{
get { return driveD; }
set
{
driveD = value;
OnPropertyChanged(nameof(DriveD));
}
}

public string DriveI
{
get { return driveI; }
set
{
driveI = value;
OnPropertyChanged(nameof(DriveI));
}
}

public string DriveH
{
get { return driveH; }
set
{
driveH = value;
OnPropertyChanged(nameof(DriveH));
}
}

private List<Tuple<string, double>> diskUsage()
{
var result = new List<Tuple<string, double>>(4);

DriveInfo[] allDrives = DriveInfo.GetDrives()
.Where(p => p.DriveType == DriveType.Fixed)
.Select(p => p).ToArray();

foreach (DriveInfo d in allDrives)
{
var driveName = d.Name.Substring(0, 2);

if (d.IsReady == true)
{
var freeSpace = Math.Round(d.TotalFreeSpace / Math.Pow(1024, 3));

result.Add(new Tuple<string, double>(driveName, freeSpace));
}
}
return result;
}

public Drives()
{
var result = diskUsage();

int size = result.Count;

switch (size)
{
case 4:
DriveC = result[0].Item2.ToString();
DriveD = result[1].Item2.ToString();
DriveI = result[2].Item2.ToString();
DriveH = result[3].Item2.ToString();
break;

case 3:
DriveC = result[0].Item2.ToString();
DriveD = result[1].Item2.ToString();
DriveI = result[2].Item2.ToString();
DriveH = "0";
break;

case 2:
DriveC = result[0].Item2.ToString();
DriveD = result[1].Item2.ToString();
DriveI = "0";
DriveH = "0";
break;

case 1:
DriveC = result[0].Item2.ToString();
DriveD = "0";
DriveI = "0";
DriveH = "0";
break;

default:
break;
}
}
}

mm8 mm8
Answer Source

You are creating a new Drives object in your DrivesCheck() method. This new object has no connection to your StackPanel.

You could re-set the DataContext of the StackPanel:

private void DrivesCheck()
{
    stkDrives.DataContext = new Drives();
}

You should also use a DispatcherTimer to make sure that you are accessing the StackPanel on the UI thread:

public MainWindow()
{
    InitializeComponent();

    System.Windows.Threading.DispatcherTimer myTimer = new System.Windows.Threading.DispatcherTimer();
    myTimer.Interval = TimeSpan.FromSeconds(1);
    myTimer.Start();
    myTimer.Tick += timeToCall;
}

private void timeToCall(object sender, EventArgs e)
{
    DrivesCheck();
}

Or you could refactor your code by for example moving the code from the constructor of the Drives class into a public method that you can call from the DrivesCheck() method:

private void DrivesCheck()
{
    Drives d = this.Resources["drivesUsage"] as Drives;
    d.Update(); //<-- this method should update the Drive* properties
}