WPF: TextBlock AutomationProperties.HelpText change - c#

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>

Related

INotifyPropertyChanged in wpf application of List

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.

Access InkToolbar placed in parent element from programmatically generated child elements

I have a UserControl which contains a InkToolbar. In this control I'm generating another set of UserControls based on user input. In each of those programmatically generated controls, contains a InkCanvas.
This is the parent UserControl & the code behind.
Main.xaml
<UserControl
x:Class="uwp.Main"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:uwp_mvvm"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0">
<InkToolbar x:Name="InkToolbar" Background="Black" Margin="10 0 0 0" VerticalAlignment="Center"/>
</StackPanel>
<ScrollViewer ZoomMode="Enabled" Background="DarkGray" x:Name="ScrollViewer" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" MinZoomFactor="0.25" Width="Auto" Height="Auto" MaxZoomFactor="4" Grid.Row="1">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
<ItemsControl x:Name="PagesItemsControl" ItemsSource="{x:Bind Pages, Mode=OneWay}" HorizontalAlignment="Center" VerticalAlignment="Top">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:TestControl x:Name="TestControl" Page="{Binding}" Margin="4 4" InkToolbarControl="{Binding Path=InkToolbar, ElementName=InkToolbar}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</Grid>
</UserControl>
Main.xaml.cs
public sealed partial class MainViewer : UserControl
{
public MainViewer()
{
this.InitializeComponent();
}
private void EventTriggerChanged(object sender, PropertyChangedEventArgs e)
{
var trigger = sender as EventTriggerTestControl;
if (trigger != null)
RaiseChanged(e.PropertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaiseChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
}
private PdfDocument pdfDocument = null;
private string password = string.Empty;
private ObservableCollection<PdfPage> pages = new ObservableCollection<PdfPage>();
public string Password
{
private get { return password; }
set { if (value != password) { password = value; } }
}
public ObservableCollection<PdfPage> Pages
{
get { return pages; }
set { if (value != pages) { pages = value; } }
}
public StorageFile File
{
get { return (StorageFile)GetValue(FileProperty); }
set { SetValue(FileProperty, value); }
}
public static readonly DependencyProperty FileProperty =
DependencyProperty.Register(nameof(File), typeof(StorageFile), typeof(MainViewer), new PropertyMetadata(null, new PropertyChangedCallback(OnDocumentChanged)));
private static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (object.Equals(e.NewValue, e.OldValue) || e.NewValue is null)
return;
d.RegisterPropertyChangedCallback(FileProperty, CaptureDocument);
}
private async static void CaptureDocument(DependencyObject sender, DependencyProperty dp) => await (sender as MainViewer).OpenFileAsync(sender.GetValue(dp) as StorageFile);
private async Task OpenFileAsync(StorageFile file)
{
try
{
if (file == null)
throw new ArgumentNullException(nameof(file));
var files = await ApplicationData.Current.LocalFolder.GetFilesAsync(CommonFileQuery.OrderByName);
if (files.Where(x => x.Name == file.Name).ToList().Count == 0)
await file.CopyAsync(ApplicationData.Current.LocalFolder, file.Name, NameCollisionOption.ReplaceExisting);
file = await ApplicationData.Current.LocalFolder.GetFileAsync(file.Name);
pdfDocument = new PdfDocument(file.Path, Password);
pdfDocument.Pages.ToList().ForEach(x => Pages.Add(x));
}
catch (Exception ex) { throw ex; }
}
}
I want to use the InkToolbar which is in the parent control inside the TestControl
TestControl.xaml
<UserControl
x:Class="uwp.TestControl"
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="using:uwp_mvvm.ViewModels" xmlns:converter="using:uwp_mvvm"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<UserControl.Resources>
<converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
</UserControl.Resources>
<Grid x:Name="Viewport" VerticalAlignment="Top" HorizontalAlignment="Center">
<Border x:Name="ViewportBorder" Background="White" BorderThickness="2, 2, 2, 2" BorderBrush="#FF353334" />
<Image x:Name="Image" Margin="0" UseLayoutRounding="True" ManipulationMode="Scale"/>
<Canvas x:Name="SelectionCanvas" CompositeMode="MinBlend" Opacity="1" />
<InkCanvas x:Name="InkCanvas" />
</Grid>
</UserControl>
TastControl.xaml.cs
public sealed partial class TestControl : UserControl
{
private const float DPI = 256;
public PdfPage Page
{
get { return (PdfPage)GetValue(PageProperty); }
set { SetValue(PageProperty, value); }
}
public static readonly DependencyProperty PageProperty =
DependencyProperty.Register(nameof(Page), typeof(PdfPage), typeof(TestControl), new PropertyMetadata(null, new PropertyChangedCallback(OnPageChanged)));
private static void OnPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (object.Equals(e.NewValue, e.OldValue) || e.NewValue is null)
return;
d.RegisterPropertyChangedCallback(PageProperty, CapturePage);
}
private static void CapturePage(DependencyObject sender, DependencyProperty dp) => (sender as TestControl).RenderPage(sender.GetValue(dp) as PdfPage);
public InkToolbar InkToolbarControl
{
get { return (InkToolbar)GetValue(InkToolbarControlProperty); }
set { SetValue(InkToolbarControlProperty, value); }
}
public static readonly DependencyProperty InkToolbarControlProperty =
DependencyProperty.Register(nameof(InkToolbarControl), typeof(InkToolbar), typeof(TestControl), new PropertyMetadata(null));
public TestControl()
{
this.InitializeComponent();
ConfigureInkCanvas();
}
private void ConfigureInkCanvas()
{
InkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Touch;
InkCanvas.InkPresenter.InputProcessingConfiguration.Mode = InkInputProcessingMode.None;
InkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(
new InkDrawingAttributes
{
IgnorePressure = false,
FitToCurve = true,
Color = Colors.Black,
PenTip = PenTipShape.Circle
});
InkCanvas.InkPresenter.UnprocessedInput.PointerEntered -= InkCanvasUnprocessedInputPointerEntered;
InkCanvas.InkPresenter.UnprocessedInput.PointerEntered += InkCanvasUnprocessedInputPointerEntered;
InkCanvas.InkPresenter.UnprocessedInput.PointerExited -= InkCanvasUnprocessedInputPointerExited;
InkCanvas.InkPresenter.UnprocessedInput.PointerExited += InkCanvasUnprocessedInputPointerExited;
}
private void InkCanvasUnprocessedInputPointerExited(InkUnprocessedInput sender, PointerEventArgs args)
{
//InkCanvas.InkPresenter.InputProcessingConfiguration.Mode = InkInputProcessingMode.Inking;
//InkToolbarControl.TargetInkCanvas = InkCanvas;
}
private void InkCanvasUnprocessedInputPointerEntered(InkUnprocessedInput sender, PointerEventArgs args)
{
//InkCanvas.InkPresenter.InputProcessingConfiguration.Mode = InkInputProcessingMode.None;
//InkToolbarControl.TargetInkCanvas = null;
}
private void RenderPage(PdfPage page, float dpi = DPI)
{
if (page is null)
return;
this.DataContext = page;
var width = (DataContext as PdfPage).Width;
var height = (DataContext as PdfPage).Height;
var writableBitmap = new WriteableBitmap((int)(width / 72f * dpi), (int)(height / 72f * dpi));
(DataContext as PdfPage).Render(writableBitmap, PageOrientations.Normal, RenderingFlags.Annotations);
Image.Width = width;
Image.Height = height;
Image.Source = writableBitmap;
SelectionCanvas.Width = width;
SelectionCanvas.Height = height;
InkCanvas.Width = width;
InkCanvas.Height = height;
Canvas.SetZIndex(Image, 0);
ConfigureInkCanvas();
}
}
I tried to use a DependencyProperty to send the InkToolbar to the TestControl but it didn't seem to work.
Could someone please help me with this? I'm not sure whether it is possible to set the TargetInkCanvas like this also. So, if there is a better approach for this any suggestions are also appreciated.
Any help is much appreciated. Thanks.
Access InkToolbar placed in parent element from programmatically generated child elements
Above solution that use DependencyProperty to pass parent InkToolBar is correct. However you bind wrong value for InkToolbarControl property.
Please bind ElementName without path directly.
<local:TestControl
x:Name="TestControl"
Margin="4,4"
InkToolbarControl="{Binding ElementName=InkToolbar}" />
Then update InkCanvasUnprocessedInputPointerExited and InkCanvasUnprocessedInputPointerEntered process logic.
private void InkCanvasUnprocessedInputPointerExited(InkUnprocessedInput sender, PointerEventArgs args)
{
InkCanvas.InkPresenter.InputProcessingConfiguration.Mode = InkInputProcessingMode.Inking;
InkToolbarControl.TargetInkCanvas = null;
}
private void InkCanvasUnprocessedInputPointerEntered(InkUnprocessedInput sender, PointerEventArgs args)
{
InkCanvas.InkPresenter.InputProcessingConfiguration.Mode = InkInputProcessingMode.None;
InkToolbarControl.TargetInkCanvas = InkCanvas;
}

Frame navigation issue while using Drawer Layout in windows phone 8.1

I am developing a Windows Phone App 8.1 using a third party DrawerLayout.
My problem is when I have design the page using draweralayout. I need to pass the same layout to each other pages in my application. but when i use bold Frame.navigate(typeof(Page1)); the application is crashed.
I am unable to debug the problem.
please help!!!
My Xaml Code is
<Grid x:Name="Rootlayout">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- title bar-->
<Grid x:Name="TitleBar" Height="50" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Background="SkyBlue">
<Image Source="/Assets/fs-logo.png" Height="50" HorizontalAlignment="Left" Margin="10,0,0,0"/>
</Grid>
<!--Drawer Layout-->
<drawer:DrawerLayout x:Name="DrawerLayout" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<!--Main Layout-->
<Grid x:Name="mainLayout" Background="White" >
<ScrollViewer>
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Image x:Name="mainImage" Grid.Row="0" Source="{Binding }"></Image>
<GridView x:Name="grdView" Grid.Row="1" ItemsSource="{Binding }" ItemContainerStyle="{StaticResource GridViewItemContainer}" Tapped="grdView_Tapped" >
<GridView.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="Blue">
<Grid Height="{Binding Size}"
Width="{Binding Size}">
<Image Height="120" Width="150" Source="{Binding sImageURL}" Stretch="Fill">
</Image>
<TextBlock Text="{Binding Name}" HorizontalAlignment="Center" VerticalAlignment="Bottom" Foreground="Black" FontSize="18"/>
</Grid>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal"></ItemsWrapGrid>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</Grid>
</ScrollViewer>
</Grid>
<!--Drawer Layout-->
<Grid x:Name="listFragment" FlowDirection="LeftToRight" >
<ListView x:Name="listItem" SelectionChanged="listItem_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontSize="20" Foreground="Black" Margin="10" VerticalAlignment="Center" HorizontalAlignment="Left"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</drawer:DrawerLayout>
<Image Grid.Row="1" Grid.Column="0" Height="50" Width="50" Source="/Assets/appbar.right.png" Tapped="chevlon_Tapped">
</Image>
</Grid>
My MainPage.cs
public sealed partial class MainPage : Page
{
private NavigationHelper navigationHelper;
private ObservableDictionary defaultViewModel = new ObservableDictionary();
List<Services.Company> companyData;
List<Services.Category> categoryData;
public MainPage()
{
this.InitializeComponent();
Loaded += MainPage_Loaded;
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
this.NavigationCacheMode = NavigationCacheMode.Required;
}
async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
await Windows.UI.ViewManagement.StatusBar.GetForCurrentView().HideAsync();
// SetGridViewItemSize();
//throw new NotImplementedException();
}
/// <summary>
/// Gets the <see cref="NavigationHelper"/> associated with this <see cref="Page"/>.
/// </summary>
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
/// <summary>
/// Gets the view model for this <see cref="Page"/>.
/// This can be changed to a strongly typed view model.
/// </summary>
public ObservableDictionary DefaultViewModel
{
get { return this.defaultViewModel; }
}
private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
if (e.PageState != null)
{
// this.DefaultViewModel["MainPage"] = (MainPage)e.PageState["MainPage"];
// Restore scroll offset
var index = (int)e.PageState["FirstVisibleItemIndex"];
var container = grdView.ContainerFromIndex(index);
grdView.ScrollIntoView(container);
}
else
{
//Load Data for the First time
DrawerLayout.InitializeDrawerLayout();
var company = await Services.CompCatInfo.GetCompaniesAsync(App.compId);
companyData = company.ToList();
if (companyData.Count > 0)
{
mainImage.Source = new BitmapImage(new Uri(companyData.ElementAt(0).LogoImgUrl, UriKind.Absolute));
}
var category = await Services.CompCatInfo.GetCategoriesAsync(App.compId);
categoryData = category.ToList();
SetGridViewItemSize();
if (categoryData.Count > 0)
{
grdView.ItemsSource = categoryData;
}
}
}
private void NavigationHelper_SaveState(object sender, SaveStateEventArgs e)
{
var isp = (ItemsWrapGrid)grdView.ItemsPanelRoot;
int firstVisibleItem = isp.FirstVisibleIndex;
e.PageState["FirstVisibleItemIndex"] = firstVisibleItem;
// e.PageState["HomePage"] = this.Frame.CurrentSourcePageType;
// This must be serializable according to the SuspensionManager
//e.PageState["MainPage"] = this.DefaultViewModel["MainPage"];
}
private void SetGridViewItemSize()
{
//var viewModel = (DataContext as ViewModel1);
var width = Math.Truncate((grdView.ActualWidth - 10));
var height = Math.Truncate((grdView.ActualHeight - 10));
// total left + right margin for each tile (ItemContainerStyle)
var margin = 10;
var twoColumnGridPortrait = (width / 2) - margin;
// var threeColumnGridPortrait = (width / 3) - margin;
var twoColumnGridLandscape = (height / 2) - margin;
var threeColumnGridLandscape = (height / 3) - margin;
double portrait;
double landscape;
portrait = twoColumnGridPortrait;
landscape = threeColumnGridLandscape;
Application.Current.Resources["GridViewItemPortrait"] = Math.Round(portrait, 2);
Application.Current.Resources["GridViewItemLandscape"] = Math.Round(landscape, 2);
if (DisplayInformation.GetForCurrentView().CurrentOrientation == DisplayOrientations.Portrait)
{
Measure((double)Application.Current.Resources["GridViewItemPortrait"]);
}
else
{
Measure((double)Application.Current.Resources["GridViewItemLandscape"]);
}
// throw new NotImplementedException();
}
private void Measure(double size)
{
for (int i = 0; i < categoryData.Count; i++)
{
var item = categoryData[i];
item.Size = size;
}
//throw new NotImplementedException();
}
#region NavigationHelper registration
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached.
/// This parameter is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.navigationHelper.OnNavigatedTo(e);
//if (e.NavigationMode == NavigationMode.New)
//{
// DrawerLayout.InitializeDrawerLayout();
// var company = await Services.CompCatInfo.GetCompaniesAsync(App.compId);
// companyData = company.ToList();
// if (companyData.Count > 0)
// {
// mainImage.Source = new BitmapImage(new Uri(companyData.ElementAt(0).LogoImgUrl, UriKind.Absolute));
// }
// var category = await Services.CompCatInfo.GetCategoriesAsync(App.compId);
// categoryData = category.ToList();
// SetGridViewItemSize();
// grdView.ItemsSource = categoryData;
//}
//else
//{
// // this.Frame.Content = e.Content;
//}
// TODO: Prepare page for display here.
// TODO: If your application contains multiple pages, ensure that you are
// handling the hardware Back button by registering for the
// Windows.Phone.UI.Input.HardwareButtons.BackPressed event.
// If you are using the NavigationHelper provided by some templates,
// this event is handled for you.
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
this.navigationHelper.OnNavigatedFrom(e);
}
#endregion
private void chevlon_Tapped(object sender, TappedRoutedEventArgs e)
{
string[] list = new string[] { "Services","Solutions","Technologies","About Us"};
listItem.ItemsSource = list.ToList();
if(DrawerLayout.IsDrawerOpen)
{
DrawerLayout.CloseDrawer();
}
else
{
DrawerLayout.OpenDrawer();
}
}
private void listItem_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (listItem.SelectedItem != null)
{
var selecteditem = listItem.SelectedValue as string;
// DetailsTxtBlck.Text = "SelectedItem is: " + selecteditem;
DrawerLayout.CloseDrawer();
listItem.SelectedItem = null;
}
}
private void grdView_Tapped(object sender, TappedRoutedEventArgs e)
{
DrawerLayout.CloseDrawer();
if(e.OriginalSource.GetType().ToString()=="Windows.UI.Xaml.Controls.Image"||e.OriginalSource.GetType().ToString()=="Windows.UI.Xaml.Controls.TextBlock")
{
var catObj = (sender as GridView).SelectedItem as Services.Category;
////Frame rootFrame = Window.Current.Content as Frame;
////rootFrame.Navigate(typeof(Page2),catObj);
Frame.Navigate(typeof(Page2),catObj);
}
}
}
You can navigate by this way
CoreDispatcher dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
(Window.Current.Content as Frame).Navigate(typeof(Page name));
});

Problems with building a quiz game on Windows Phone 7.1

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.

Report progress in a WPF progress bar for the following method(s) in a button click?

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]

Categories

Resources