I'm busy with a little project on Windows Phone 7.1. I'm trying to build a quiz based game. I'm using C# in Visual Studio 2010.
I want the questions and answers to appear in random order. There appears an image with each question and after five questions the game is finished. If you have made one mistake you are a loser.
This is what I got so far:
namespace PhoneApp3
{
public partial class MainPage : PhoneApplicationPage
{
private List<Quiz> _Questions = null;
private const int _QuestionCount = 7;
private Quiz _CurrentQuestion = null;
private int _CurrentQuestionIndex = 0;
private int _Missed = 0;
private DateTime _StartTime = DateTime.UtcNow;
public MainPage()
{
InitializeComponent();
_Questions = new List<Quiz>();
_Questions.Add(new Quiz("What is the height of the Eiffel Tower?", "324 meters", "/PhoneApp3/Images/Eiffel_Tower.png", new[] { "176 meters", "257 meters" }));
_Questions.Add(new Quiz("Where can you find the Big Ben?", "In London", "/PhoneApp3/Images/Big_Ben.png", new[] { "In Berlin", "In Copenhagen" }));
_Questions.Add(new Quiz("Where can you find the Tower of Pisa?", "In Italy", "/PhoneApp3/Images/Tower_of_Pisa.png", new[] { "In Spain", "In Greece" }));
_Questions.Add(new Quiz("What is the largest city of Europe?", "Istanbul", "/PhoneApp3/Images/City.png", new[] { "Moscow", "London" }));
_Questions.Add(new Quiz("Which country is the host of the next UEFA European Championship (2016)?", "France", "/PhoneApp3/Images/UEFA.png", new[] { "Spain", "Germany" }));
_Questions.Add(new Quiz("Who painted The Night Watch?", "Rembrandt van Rijn", "/PhoneApp3/Images/Night_Watch.png", new[] { "Vincent van Gogh", "Pablo Picasso" }));
_Questions.Add(new Quiz("Which is the smallest country of Europe?", "Iceland", "/PhoneApp3/Images/Eiffel_Tower.png", new[] { "Greece", "The Netherlands" }));
StartGame();
}
private void StartGame()
{
_CurrentQuestion = null;
_CurrentQuestionIndex = 0;
_Missed = 0;
_StartTime = DateTime.UtcNow;
_Questions = Shuffle(_Questions);
}
private List<T> Shuffle<T>(List<T> inputList)
{
List<T> randomList = new List<T>(inputList.Count);
var random = new Random();
int randomIndex = 0;
while (inputList.Count > 0)
{
randomIndex = random.Next(0, inputList.Count);
randomList.Add(inputList[randomIndex]);
inputList.RemoveAt(randomIndex);
}
return randomList;
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.NavigationMode == System.Windows.Navigation.NavigationMode.Back)
{
StartGame();
}
}
private void btnOption1_Click(object sender, RoutedEventArgs e)
{
CheckResult((sender as Button).Content);
}
private void btnOption2_Click(object sender, RoutedEventArgs e)
{
CheckResult((sender as Button).Content);
}
private void btnOption3_Click(object sender, RoutedEventArgs e)
{
CheckResult((sender as Button).Content);
}
private void CheckResult(object obj)
{
var choice = obj as string;
if (!choice.Equals(_CurrentQuestion.Answer))
{
++_Missed;
}
MoveToNext();
}
private void MoveToNext()
{
if (_CurrentQuestionIndex < 5)
{
// continue with next question
_CurrentQuestion = _Questions[_CurrentQuestionIndex++];
img.Source = new BitmapImage(new Uri(_CurrentQuestion.ImagePath, UriKind.RelativeOrAbsolute));
var opties = Shuffle(new List<string>() { _CurrentQuestion.Options[0], _CurrentQuestion.Options[1], _CurrentQuestion.Answer });
btnOption1.Content = opties[0];
btnOption2.Content = opties[1];
btnOption3.Content = opties[2];
}
else
{
// end of the game
var time = DateTime.UtcNow.Subtract(_StartTime);
if (_Missed > 0)
{
NavigationService.Navigate(new Uri("/Result.xaml?win=false&time=" + time, UriKind.Relative));
}
else
{
NavigationService.Navigate(new Uri("/Result.xaml?win=true&time=" + time, UriKind.Relative));
}
}
}
}
public class Quiz
{
public string Question { get; set; }
public string Answer { get; set; }
public string ImagePath { get; set; }
public string[] Options { get; set; }
public Quiz(string question, string answer, string imagePath, string[] options)
{
Question = question;
Answer = answer;
ImagePath = imagePath;
Options = options;
}
}
}
But it doesn't work how I thought it would do. Questions and options don't appear, the whole game doesn't work. I can't figure out what I have to do, so I'm pretty stuck.
EDIT
The XAML
<phone:PhoneApplicationPage
x:Class="PhoneApp3.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="800" d:DesignWidth="480"
shell:SystemTray.IsVisible="False">
<Grid x:Name="LayoutRoot" Background="#FF008FFF">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="TitlePanel" Margin="12,17,12,28" Height="0"></StackPanel>
<Grid x:Name="ContentPanel" Grid.RowSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" Height="Auto" Margin="0,36,12,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="227*" />
<ColumnDefinition Width="229*" />
</Grid.ColumnDefinitions>
<Button Background="#FF3EA6FF" BorderBrush="Transparent" Grid.ColumnSpan="2" Height="72" HorizontalAlignment="Left" Margin="12,656,12,0" Name="btnOption3" VerticalAlignment="Top" Width="480" Click="btnOption3_Click" />
<Button Background="#FF3EA6FF" BorderBrush="Transparent" Grid.ColumnSpan="2" Height="72" HorizontalAlignment="Left" Margin="12,500,12,0" Name="brnOption1" VerticalAlignment="Top" Width="480" Click="btnOption1_Click" />
<Button Background="#FF3EA6FF" BorderBrush="Transparent" Grid.ColumnSpan="2" Height="72" HorizontalAlignment="Left" Margin="12,578,12,0" Name="btnOption2" VerticalAlignment="Top" Width="480" Click="btnOption2_Click" />
<Image Height="276" HorizontalAlignment="Center" Margin="96,218,96,0" Name="img" Stretch="Fill" VerticalAlignment="Top" Width="276" Grid.ColumnSpan="2" />
</Grid>
</Grid>
The problem: I don't get any data to my screen, there appears no text in the buttons e.g.
Related
Problem
I want to refresh my wpf view when a change is made in a List of objects in my application, but it wont register the INotifyChanged method when I change a value.
What I've tried
I went to multiple different stackoverflow pages with sort of the same problem but I don't get it working right. It wont register a change in a object in the list.
my code
below is the code for the MainWindow of the WPF application in wher with the last button click I change the value of XLocation in an object out of a list.
public partial class MainWindow : Window
{
private string filePathArtist { get; set; }
private string filePathGrid { get; set; }
public Game.Game Game { get; set; }
public MainWindow()
{
InitializeComponent();
filePathGrid = String.Empty;
filePathArtist = String.Empty;
}
private void BtnOpen_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
bool? res = openFileDialog.ShowDialog();
if (res == true)
{
string filepathgrid = openFileDialog.FileName;
filePathGrid = filepathgrid;
GridTextBox.Text = filepathgrid;
}
}
private void PickArtistBtn_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
bool? res = openFileDialog.ShowDialog();
if (res == true)
{
string filepathartist = openFileDialog.FileName;
filePathArtist = filepathartist;
ArtistTextBox.Text = filepathartist;
}
}
private void CreateGridBtn_Click(object sender, RoutedEventArgs e)
{
Game = new Game.Game(filePathGrid, filePathArtist);
this.DataContext = Game;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Game.Artists[0].XLocation = 30;
}
}
Next code is the Game class where is implemented a INotfyPropertyChanged on the list of Artists.
public class Game : INotifyPropertyChanged
{
public List<Artist> _artists;
public List<Artist> Artists
{
get
{
return _artists;
}
set
{
_artists = value;
OnPropertyChanged("Artists");
}
}
public List<ITile> Tiles { get; set; }
public Game()
{
}
public Game(string graphPath, string artistPath)
{
IDataParser graphParser = DataFactory.DataFactory.Instance.CreateParser(graphPath);
IDataParser artistParser = DataFactory.DataFactory.Instance.CreateParser(artistPath);
Tiles = graphParser.ParseGridData(graphPath);
Artists = artistParser.ParseArtistData(artistPath);
Test = "new Game";
}
public string Test { get; set; } = "t";
public event PropertyChangedEventHandler? PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Ive also added the INotifyPropertyChanged in the Artists class
public class Artist : INotifyPropertyChanged
{
private float _xLocation;
private float _yLocation;
private int _xVelocity;
private int _yVelocity;
public float XLocation
{
get => _xLocation;
set
{
_xLocation = value;
OnPropertyChanged("XLocation");
}
}
public float ConvertedXLoc
{
get => XLocation * (float)3.75;
set { }
}
public float YLocation
{
get => _yLocation;
set
{
_yLocation = value;
OnPropertyChanged("YLocation");
}
}
public float ConvertedYLoc
{
get => YLocation * (float)3.75;
set { }
}
public int XVelocity
{
get => _xVelocity;
set
{
_xVelocity = value;
}
}
public int YVelocity
{
get => _yVelocity;
set
{
_yVelocity = value;
}
}
public event PropertyChangedEventHandler? PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then here is the Xaml code where I bind the objects to the wpf UI.
<Window x:Class="BroadwayBoogie.MainWindow"
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:game="clr-namespace:BroadwayBoogie.Game"
mc:Ignorable="d"
Title="MainWindow" Height="900" Width="900"
>
<Window.DataContext>
<game:Game/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="201*"/>
<ColumnDefinition Width="199*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="21*"/>
<RowDefinition Height="401*"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<Button x:Name="BtnOpen" Content="Pick Grid" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Click="BtnOpen_Click"/>
<TextBox x:Name="GridTextBox" HorizontalAlignment="Center" TextWrapping="NoWrap" VerticalAlignment="Center" Width="266" />
<Button x:Name="PickArtistBtn" Content="Pick Artist" HorizontalAlignment="Left" Margin="356,0,0,0" VerticalAlignment="Center" Click="PickArtistBtn_Click" RenderTransformOrigin="-0.135,0.647"/>
<TextBox x:Name="ArtistTextBox" HorizontalAlignment="Left" Margin="30,14,0,0" TextWrapping="NoWrap" VerticalAlignment="Top" Width="231" Grid.Column="1"/>
<Button x:Name="CreateGridBtn" Grid.Column="1" Content="Create Grid" HorizontalAlignment="Left" Margin="311,14,0,0" VerticalAlignment="Top" Click="CreateGridBtn_Click"/>
<Canvas Width="800" Height="800" Grid.ColumnSpan="2" Margin="49,15,51,27" Grid.Row="1" Background="DarkSeaGreen" Grid.RowSpan="2">
<ItemsControl Name="tilesItemsControl" ItemsSource="{Binding Tiles}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<Rectangle
Width="15"
Height="15"
Fill="{Binding Color}"
Canvas.Left ="{Binding ConvertedXLoc}"
Canvas.Top="{Binding ConvertedYLoc}" />
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Name="ArtistItemsControl" ItemsSource="{Binding Artists}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<Rectangle
Width="3.75"
Height="3.75"
Fill="Black"
Canvas.Left ="{Binding ConvertedXLoc}"
Canvas.Top="{Binding ConvertedYLoc}" />
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
<Grid/>
<Button Content="Button" HorizontalAlignment="Left" Margin="4,0,0,0" Grid.Row="2" VerticalAlignment="Center" Click="Button_Click"/>
</Grid>
So with the press of the button I added for testing purposes. It changes a value in the List and then the PropertyChanged method should detect that but it doesn't detect it it just skips it.
Question
So my basic question is, how do I detect the change of a property from objects out of the List of Artists.
OnPropertyChanged will only be executed when the property itself is changed. A new item in a list is not a property change. That's the reason why no updates happens.
Instead a List, try an ObservableCollection. An ObservableCollection implements an additional INotifyCollectionChanged which makes the UI able to react on changing items in the list.
I want to create an accessible application. I want to show pdf files in TextBlock page by page but when I change the CurrentPage properties on EbookViewerViewModel the NVDA Screen Reader doesn't read It the text again. I thought if I set focus again the Screen Reader will read it, but doesn't. Can I solve this problem somehow?
The EbookViewerWindow.xaml contains this:
<Window x:Class="myproj.EbookViewerWindow"
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:myproj"
mc:Ignorable="d"
Title="AEBCA - Könyv megjelenítő" Height="300" Width="300" Closed="Window_Closed" WindowStartupLocation="CenterScreen" KeyDown="Window_KeyDown">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" x:Name="AuthorTB" Text="{Binding Author}" AutomationProperties.Name="A könyv szerzője" AutomationProperties.HelpText="{Binding Author}" Focusable="True"></TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" x:Name="BookNameTB" Text="{Binding BookName}" AutomationProperties.Name="A könyv címe" AutomationProperties.HelpText="{Binding BookName}" Focusable="True"></TextBlock>
<DockPanel Grid.Row="1" Grid.ColumnSpan="2">
<TextBlock x:Name="CurrentPageTB" Text="{Binding CurrentPage}" AutomationProperties.Name="A könyv tartalma" AutomationProperties.HelpText="{Binding CurrentPage}" Focusable="True"></TextBlock>
<Label x:Name="help"></Label>
</DockPanel>
</Grid>
In the code behind I do this:
public partial class EbookViewerWindow : Window
{
EbookViewerViewModel vm;
int index = 0;
PdfReader pdfReader;
public EbookViewerWindow()
{
InitializeComponent();
vm = new EbookViewerViewModel();
DataContext = vm;
vm.Author = "Antoine de Saint-Exupéry";
vm.BookName = "A Kis Herceg";
AuthorTB.Focus();
string fileName = #"d:\pdf.pdf";
if (File.Exists(fileName))
{
pdfReader = new PdfReader(fileName);
NextPage(pdfReader);
}
}
private void Window_Closed(object sender, EventArgs e)
{
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if(e.Key == Key.Right)
{
NextPage(pdfReader);
CurrentPageTB.Focus();
e.Handled = true;
}
if(e.Key == Key.Left)
{
PreviousPage(pdfReader);
CurrentPageTB.Focus();
e.Handled = true;
}
}
private void NextPage(PdfReader pdfReader)
{
index++;
int pages = pdfReader.NumberOfPages;
ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
string currentText = index.ToString() + ". oldal \n";
string temp = PdfTextExtractor.GetTextFromPage(pdfReader, index, strategy);
currentText += temp;
vm.CurrentPage = currentText;
}
private void PerviousPage(PdfReader pdfReader)
{
index--;
int pages = pdfReader.NumberOfPages;
ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
string currentText = index.ToString() + ". oldal \n";
string temp = PdfTextExtractor.GetTextFromPage(pdfReader, index, strategy);
currentText += temp;
vm.CurrentPage = currentText;
}
}
}
My EbookViewerViewModel:
public class EbookViewerViewModel : BaseViewModel
{
private string currentPage;
public string BookName { get; set; }
public string Author { get; set; }
public string CurrentPage { get { return currentPage; } set { currentPage = value; OnPropertyChanged(); } }
}
I found answer here:
How do I make screen readers read my WPF message similarly to how they read Win32 MessageBox?
<DockPanel Grid.Row="1" Grid.ColumnSpan="2">
<TextBlock x:Name="CurrentPageTB" Text="{Binding CurrentPage}" Focusable="True">
<Run AutomationProperties.Name="A könyv tartalma" AutomationProperties.HelpText="{Binding CurrentPage}"></Run>
</TextBlock>
</DockPanel>
I cant get nice reorder animation in ListView - also tried overriding all the various internal transitions via the <Transitions><TransitionsCollection>... property. Nothing. Tried using simple .Insert/.Remove and .Move and neither works nicely.
Insert/Remove has animation for the items that happen to have been pushed around and .Move acts like a Reset on the collection.
The following code in MainPage:
public sealed partial class MainPage : Page
{
private ObservableCollection<Holder> Items = new ObservableCollection<Holder>(Enumerable.Range(0, 10).Select(x => new Holder(x)));
public MainPage()
{
this.InitializeComponent();
Items.CollectionChanged += Items_CollectionChanged;
}
private void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
}
private Random _random = new Random();
private bool asc = true;
private void Button_Click(object sender, RoutedEventArgs e)
{
IList<Holder> temp;
//var temp = Items.Skip(1).Union(Items.Take(1)).ToList();
// var temp = new Holder[] { Items.ElementAt(Items.Count - 1) }.Union(Items.Take(Items.Count - 1)).ToList();
temp = Items.OrderBy(x => _random.NextDouble()).ToList();
// temp = asc ? Items.OrderBy(x => x.I).ToList() : Items.OrderByDescending(x => x.I).ToList();
asc = !asc;
for (int i = 0; i < temp.Count; i++)
{
if (Items[i] != temp[i])
{
// When using Move, no animation occurs at all. When using Remove/Insert, there's partial
// animation for the items that happened to stay at the same place.
//Items.Move(Items.IndexOf(temp[i]), i);
Items.Remove(temp[i]);
Items.Insert(i, new Holder(temp[i].I));
}
}
}
}
public class Holder
{
public Holder(int i)
{
I = i;
}
public string Name => "Hey hey hey " + I.ToString();
public int I { get; private set; }
public override bool Equals(object obj)
{
return ((Holder)obj).I == I;
}
public override int GetHashCode()
{
return I.GetHashCode();
}
}
With the following XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Content="Mix" Click="Button_Click" Grid.Row="1"></Button>
<ListView ItemsSource="{x:Bind Items}" >
<ListView.ItemTemplate>
<DataTemplate>
<Border BorderThickness="3" BorderBrush="Blue">
<TextBlock Text="{Binding Name}" Margin="20"/>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
I'm writing my first universal windows app and am currently experimenting with the accelerometer.
Currently I'm displaying it's reading as numbers on the screen but I would like to also show a graph that depicts these values visually.
I've seen some charting examples online but none that accept a live stream of data and display it as it comes in.
Basically what I want is a graph over the time domain that draws a line which represents the values the accelerometer outputs.
This is my first attempt at windows programming and I'm working with C#.
Is there a 'proper' way to do such a thing? a commonly accepted method?
Use WinRTXamlToolkit:
XAML:
<Page
x:Class="App3.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App3"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Charting="using:WinRTXamlToolkit.Controls.DataVisualization.Charting"
mc:Ignorable="d" Loaded="Page_Loaded">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="10*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Charting:Chart x:Name="chart1" Grid.Row="0">
<Charting:LineSeries ItemsSource="{Binding Data}"
DependentValuePath ="Accel"
IndependentValuePath ="Timestamp" Margin="0"/>
</Charting:Chart>
<Button x:Name="btnStart" Content="START" Grid.Row="1" Click="btnStart_Click" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></Button>
<Button x:Name="btnStop" Content="STOP" Grid.Row="2" Click="btnStop_Click" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></Button>
</Grid>
</Page>
MainPage:
public sealed partial class MainPage : Page
{
MyViewModel vm;
public MainPage()
{
this.InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
vm = new MyViewModel();
DataContext = vm;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
vm.Start();
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
vm.Stop();
}
}
ViewModel:
public class MyViewModel
{
private Timer accelerometer;
private Random r;
private ObservableCollection<MyAccelModel> data;
public ObservableCollection<MyAccelModel> Data { get { return data; } }
public MyViewModel()
{
data = new ObservableCollection<MyAccelModel>();
r = new Random(DateTime.Now.Millisecond);
}
public void Start()
{
accelerometer = new Timer(AccelDataCallback, null, 100, 500);
}
public void Stop()
{
accelerometer.Dispose();
}
private async void AccelDataCallback(object state)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
data.Add(new MyAccelModel { Timestamp = DateTime.Now, Accel = r.NextDouble() });
});
}
}
I have the following method that is executed in a button click:
private void CopyDirectoriesAndFiles(string source, string target, string[] excludedFolders)
{
foreach (string dir in Directory.GetDirectories(source, "*", System.IO.SearchOption.AllDirectories))
if (!excludedFolders.Contains(dir))
Directory.CreateDirectory(target + dir.Substring(source.Length));
foreach (string file_name in Directory.GetFiles(source, "*.*", System.IO.SearchOption.AllDirectories))
if (!File.Exists(Path.Combine(target + file_name.Substring(source.Length))))
File.Copy(file_name, target + file_name.Substring(source.Length));
}
The button click has some other methods, but they don't take very long to run, but even so, how can I show and update a progress bar for each even that is run. I put a textbox, but it only writes to the textbox once it is finished with everything. My button order may looks like this:
InitializeStuff();
CopyFiles();
CleanUp();
A progress bar is not absolutely necessary, although nice. It would be great if I could get my textbox to update at each time a method completed instead of at the very end.
Here's a complete working model using MVVM:
The View:
<Window x:Class="CopyFiles.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:model="clr-namespace:CopyFiles">
<Window.DataContext>
<model:CopyModel />
</Window.DataContext>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="booleanToVisibilityConverter"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Name="sourceLabel">Source</Label>
<TextBox Text="{Binding Source, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="0" Grid.Column="1" Name="sourceTextBox" Margin="5"/>
<Label Grid.Row="1" Grid.Column="0" Name="destinationLabel">Destination</Label>
<TextBox Text="{Binding Destination, Mode =TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Grid.Column="1" Name="destinationTextBox" Margin="5" />
<Button Command="{Binding CopyCommand}" Grid.Row="2" Grid.ColumnSpan="2" Content="Copy" Name="copyButton" Width="40" HorizontalAlignment="Center" Margin="5"/>
<ProgressBar Visibility="{Binding CopyInProgress, Converter={StaticResource booleanToVisibilityConverter}}" Value="{Binding Progress}" Grid.Row="3" Grid.ColumnSpan="2" Height="20" Name="copyProgressBar" Margin="5" />
</Grid>
</Window>
The ViewModel:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using Microsoft.Practices.Prism.Commands;
namespace CopyFiles
{
public class CopyModel: INotifyPropertyChanged
{
private string source;
private string destination;
private bool copyInProgress;
private int progress;
private ObservableCollection<string> excludedDirectories;
public CopyModel()
{
this.CopyCommand = new DelegateCommand(ExecuteCopy, CanCopy);
this.excludedDirectories = new ObservableCollection<string>();
}
public event PropertyChangedEventHandler PropertyChanged;
public string Source
{
get { return source; }
set
{
source = value;
RaisePropertyChanged("Source");
CopyCommand.RaiseCanExecuteChanged();
}
}
public string Destination
{
get { return destination; }
set
{
destination = value;
RaisePropertyChanged("Destination");
CopyCommand.RaiseCanExecuteChanged();
}
}
public bool CopyInProgress
{
get { return copyInProgress; }
set
{
copyInProgress = value;
RaisePropertyChanged("CopyInProgress");
CopyCommand.RaiseCanExecuteChanged();
}
}
public int Progress
{
get { return progress; }
set
{
progress = value;
RaisePropertyChanged("Progress");
}
}
public ObservableCollection<string> ExcludedDirectories
{
get { return excludedDirectories; }
set
{
excludedDirectories = value;
RaisePropertyChanged("ExcludedDirectories");
}
}
public DelegateCommand CopyCommand { get; set; }
public bool CanCopy()
{
return (!string.IsNullOrEmpty(Source) &&
!string.IsNullOrEmpty(Destination) &&
!CopyInProgress);
}
public void ExecuteCopy()
{
BackgroundWorker copyWorker = new BackgroundWorker();
copyWorker.DoWork +=new DoWorkEventHandler(copyWorker_DoWork);
copyWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(copyWorker_RunWorkerCompleted);
copyWorker.ProgressChanged += new ProgressChangedEventHandler(copyWorker_ProgressChanged);
copyWorker.WorkerReportsProgress = true;
copyWorker.RunWorkerAsync();
}
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if(handler != null)
{
var eventArgs = new PropertyChangedEventArgs(propertyName);
handler(this, eventArgs);
}
}
private void copyWorker_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
this.CopyInProgress = true;
worker.ReportProgress(0);
var directories = Directory.GetDirectories(source, "*", System.IO.SearchOption.AllDirectories);
var files = Directory.GetFiles(source, "*.*", System.IO.SearchOption.AllDirectories);
var total = directories.Length + files.Length;
int complete = 0;
foreach (string dir in directories)
{
if (!ExcludedDirectories.Contains(dir))
Directory.CreateDirectory(destination + dir.Substring(source.Length));
complete++;
worker.ReportProgress(CalculateProgress(total, complete));
}
foreach (string file_name in files)
{
if (!File.Exists(Path.Combine(destination + file_name.Substring(source.Length))))
File.Copy(file_name, destination + file_name.Substring(source.Length));
complete++;
worker.ReportProgress(CalculateProgress(total, complete));
}
}
private static int CalculateProgress(int total, int complete)
{
// avoid divide by zero error
if (total == 0) return 0;
// calculate percentage complete
var result = (double)complete / (double)total;
var percentage = result * 100.0;
// make sure result is within bounds and return as integer;
return Math.Max(0,Math.Min(100,(int)percentage));
}
private void copyWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.Progress = e.ProgressPercentage;
}
private void copyWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.CopyInProgress = false;
}
}
}
I have the following method that is executed in a button click:
It shouldn't be. This will freeze your UI for too long.
Use a Backgroundworker. [1], [2] and [3]