I have a vertical ListView using virtualization, and I subscribe to the ContainerContentChanging event. If I insert items into the top of the list, and attempt to get their transform in the handler for ContainerContentChanging using TransformToVisual(Window.Current.Content), as soon as the list has enough items to start virtualizing, I begin getting values like -15000 for the transforms matrix OffsetX and OffsetY values. Since I'm inserting the item at the top, it's clearly rendering (it has appropriate calculated width/height, etc.), so I'm not sure why the transform would be bad.
Here's some code if it helps. It doesn't get much more basic.
XAML:
<Page
x:Class="TestListView.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestListView"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="400"/>
</Grid.RowDefinitions>
<Button
Content="Add Item"
Click="OnAddItemClick"/>
<ListView
Grid.Row="1"
ItemsSource="{x:Bind Items, Mode=OneWay}"
ContainerContentChanging="OnContainerContentChanging"/>
</Grid>
</Page>
Code-Behind:
public sealed partial class MainPage : Page
{
public readonly ObservableCollection<string> Items = new ObservableCollection<string>();
public MainPage()
{
this.InitializeComponent();
Items.Add("Test 2");
Items.Add("Test 1");
Items.Add("Test 0");
}
private void OnAddItemClick(object sender, RoutedEventArgs e)
{
Items.Insert(0, $"Test {Items.Count}");
}
private void OnContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
var addedItem = args?.ItemContainer;
if (addedItem == null)
return;
var transform = addedItem.TransformToVisual(Window.Current.Content);
}
}
Related
I have a problem with a PreviewMouseDown event. I have an app that does some complex drawing in a DrawingVisual which is later added on the canvas.
I've made a minimal working demonstration of the problem.
Steps to reproduce:
Run an app
Click Render
Click inside a red area.
PreviewMouseDown event on the Canvas is firing on the green area but doesn't fire at the red one. I really need to capture the mouse events inside this red area. How can this be accomplished?
Here the XAML:
<Window x:Class="MouseDownTest.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Button Grid.Row="0" Click="Button_Click">Render</Button>
<Canvas Grid.Row="1" x:Name="HostCanvas" PreviewMouseDown="HostCanvas_PreviewMouseDown"
Background="Green"
Width="300" Height="300"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Window>
And here is the code behind:
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace MouseDownTest
{
public partial class MainWindow : Window
{
public class VisualHost : UIElement
{
public Visual Visual { get; set; }
public VisualHost(Visual childVisual) { Visual = childVisual; }
protected override int VisualChildrenCount => (Visual is null) ? 0 : 1;
protected override Visual GetVisualChild(int index) { return Visual; }
} // class VisualHost
public MainWindow() => InitializeComponent();
// Not FIRING inside a red square
private void HostCanvas_PreviewMouseDown(object sender, MouseButtonEventArgs e) => MessageBox.Show("Got a click!");
private void Render()
{
this.HostCanvas.Children.Clear();
this.HostCanvas.InvalidateVisual();
DrawingVisual vis = new();
using (DrawingContext dc = vis.RenderOpen())
{
var rect = new Rect(50, 50, 50, 50);
var redbrush = new SolidColorBrush(Colors.Red);
var redpen = new Pen(redbrush, 2);
dc.DrawRectangle(redbrush, redpen, rect);
}
var vh = new VisualHost(vis);
this.HostCanvas.Children.Add(vh);
System.Diagnostics.Debug.Assert(this.HostCanvas.Children.Count > 0);
}
private void Button_Click(object sender, RoutedEventArgs e) => Render();
}
}
So, how can I capture the mouse inside a red area and why exactly PreviewMouseDown is not firing?
I have a MainWindowViewModel and my MainWindow contains a frame to display project pages.
The first page being displayed is a list of recently opened projects(Similar to Microsoft word) which has it's own ViewModel.
There is no problem in loading the list but when I want to send the user-selected item from this list to the MainWindowViewModel I can not use Find-Ancestor to reach the Window DataContext(It looks like the frame has some restrictions).
How can I send the user-selected item to the MainWindowViewModel?
public class RecentlyOpenedFilesViewModel
{
readonly IFileHistoryService _fileHistoryService;
private ObservableCollection<RecentlyOpenedFileInfo> _RecentlyOpenedFilesList;
public ObservableCollection<RecentlyOpenedFileInfo> RecentlyOpenedFilesList
{
get { return _RecentlyOpenedFilesList; }
set { _RecentlyOpenedFilesList = value; RaisePropertyChanged(); }
}
public RecentlyOpenedFilesViewModel( IFileHistoryService fileService):base()
{
_fileHistoryService = fileService;
RecentlyOpenedFilesList=new ObservableCollection<RecentlyOpenedFileInfo>(_fileHistoryService.GetFileHistory());
}
public void RefreshList()
{
RecentlyOpenedFilesList = new ObservableCollection<RecentlyOpenedFileInfo>(_fileHistoryService.GetFileHistory());
}
}
<Page
x:Class="MyProject.Views.V3.Other.RecentlyOpenedFilesPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject.Views.V3.Other"
xmlns:vmv3="clr-namespace:MyProject"
Title="RecentlyOpenedFilesPage">
<Page.Resources>
<DataTemplate x:Key="RecentlyOpenedFileInfoTemplate"
>
<Button
Height="70"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.OpenProjectFromPathCommand}"
CommandParameter="{Binding}">
<Button.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="70" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<StackPanel
Grid.Row="0"
Grid.Column="0"
VerticalAlignment="Top">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Path}" />
</StackPanel>
<TextBlock
Grid.Row="0"
Grid.Column="1"
Margin="50,0,0,0"
VerticalAlignment="Center"
Text="{Binding DateModified}" />
</Grid>
</Button.Content>
</Button>
</DataTemplate>
</Page.Resources>
<Grid>
<ListView
ItemTemplate="{StaticResource RecentlyOpenedFileInfoTemplate}"
ItemsSource="{Binding RecentlyOpenedFilesList}" />
</Grid>
public RecentlyOpenedFilesPage(MainWindowViewModel vm)
{
this.DataContext = vm;
InitializeComponent();
}
Now I have a direct link between MainWindowViewModel and RecentlyOpenedFilesViewModel but I would like to remove this dependency and use another way of connection like(routed commands which I have a problem with)
The MainWindow contains a frame in which the RecentlyOpenedFilesPage is set to its content.
<Window
x:Class="MyProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:fw="clr-namespace:SourceChord.FluentWPF;assembly=FluentWPF" >
<Frame Name="frameMain"/></Window>
public class MainWindowViewModel : RecentlyOpenedFilesViewModel, IMainWindowViewModel
{
private void LoadRecentlyOpenedProjects()
{
CurrentView = new RecentlyOpenedFilesPage(this);
}
}
So, here is my suggested solution. It uses the basic idea to propagate the DataContext from the outside into a frame content, as presented in page.DataContext not inherited from parent Frame?
For demonstration purpose, I provide an UI with a button to load the page, a textblock to display the selected result from the list within the page and (ofcourse) the frame that holds the page.
<Window x:Class="WpfApplication1.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Name="parentGrid">
<TextBlock VerticalAlignment="Top" HorizontalAlignment="Right" Margin="5" Text="{Binding SelectedFile}" Width="150" Background="Yellow"/>
<Button VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5" Click="Button_Click" Width="150">Recent Files List</Button>
<Frame Name="frameMain" Margin="5 50 5 5"
LoadCompleted="frame_LoadCompleted"
DataContextChanged="frame_DataContextChanged"/>
</Grid>
</Window>
Viewmodel classes:
public class BaseVm : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName]string propName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
public class MyWindowVm : BaseVm
{
private string _selectedFile;
public string SelectedFile
{
get => _selectedFile;
set
{
_selectedFile = value;
OnPropertyChanged();
}
}
}
public class MyPageVm : BaseVm
{
public ObservableCollection<MyRecentFile> Files { get; } = new ObservableCollection<MyRecentFile>();
}
public class MyRecentFile
{
public string Filename { get; set; }
public string FilePath { get; set; }
}
Main code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
parentGrid.DataContext = new MyWindowVm();
}
// Load Page on some event
private void Button_Click(object sender, RoutedEventArgs e)
{
frameMain.Content = new RecentlyOpenedFilesPage(new MyPageVm
{
Files =
{
new MyRecentFile { Filename = "Test1.txt", FilePath = "FullPath/Test1.txt"},
new MyRecentFile { Filename = "Test2.txt", FilePath = "FullPath/Test2.txt"}
}
});
}
// DataContext to Frame Content propagation
private void frame_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
UpdateFrameDataContext(sender as Frame);
}
private void frame_LoadCompleted(object sender, NavigationEventArgs e)
{
UpdateFrameDataContext(sender as Frame);
}
private void UpdateFrameDataContext(Frame frame)
{
var content = frame.Content as FrameworkElement;
if (content == null)
return;
content.DataContext = frame.DataContext;
}
}
Now, the page.xaml ... notice: we will set the page viewmodel to the pageRoot.DataContext, not to the page itself. Instead we expect the page datacontext to be handled from the outside (as we do in the MainWindow) and we can reference it with the page internal name _self:
<Page x:Class="WpfApplication1.RecentlyOpenedFilesPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="RecentlyOpenedFilesPage"
Name="_self">
<Grid Name="pageRoot">
<ListView ItemsSource="{Binding Files}"
SelectedValue="{Binding DataContext.SelectedFile,ElementName=_self}"
SelectedValuePath="FilePath"
DisplayMemberPath="Filename"/>
</Grid>
</Page>
Page code behind to wire up the viewmodel:
public partial class RecentlyOpenedFilesPage : Page
{
public RecentlyOpenedFilesPage(MyPageVm myPageVm)
{
InitializeComponent();
pageRoot.DataContext = myPageVm;
}
}
As you can see, with this setup, no viewmodel knows about any involved view. The page doesn't handle the MainViewmodel, but the page requires a DataContext with a SelectedFile property to be provided from the outside.
The MainViewmodel doesn't know about the recent file list, but allows to set a selected file, no matter where it originates from.
The decision, to initialize the RecentlyOpenedFilesPage with a pre-created viewmodel is not important. You could just as well use internal logic to initialize the page with recent files, then the Mainwindow would not be involved.
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 a checkbox in my datatemplate and for some reason the events are not firing. see code below. my datatemplate is in a resource dictionary with code behind file. Any Ideas?
<ResourceDictionary x:Class="ArmyBuilder.Templates.EquipmentDataTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<DataTemplate x:Key="EquipmentDataTemplate">
<Grid>
<CheckBox Content="{Binding Name}" Checked="ToggleButton_OnChecked" Click="CheckBox_Click"/>
</Grid>
</DataTemplate>
//code behind
namespace ArmyBuilder.Templates
{
public partial class EquipmentDataTemplate : ResourceDictionary
{
public EquipmentDataTemplate()
{
InitializeComponent();
}
private void ToggleButton_OnChecked(object sender, RoutedEventArgs e)
{
// breakpoint not hit
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
// breakpoint not hit
}
}
}
I am not sure how you use it, but the your code works for me and the click event got fired. Check the following and if you still cannot find the point, share a repro project to show how you used it.
Template XAML:
<ResourceDictionary x:Class="App10.EquipmentDataTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<DataTemplate x:Key="EquipmentDataTemplate">
<Grid>
<CheckBox Content="Click Me" Checked="ToggleButton_OnChecked" Click="CheckBox_Click"/>
</Grid>
</DataTemplate>
</ResourceDictionary>
Template cs:
namespace App10
{
public sealed partial class EquipmentDataTemplate : ResourceDictionary
{
public EquipmentDataTemplate()
{
this.InitializeComponent();
}
private void ToggleButton_OnChecked(object sender, RoutedEventArgs e)
{
// breakpoint not hit
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
// breakpoint not hit
}
}
}
In MainPage.Xaml, use the template in a ListView:
<Page
x:Class="App10.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App10"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<local:EquipmentDataTemplate></local:EquipmentDataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="listView" CanReorderItems="True" AllowDrop="True" ItemTemplate="{StaticResource EquipmentDataTemplate}">
</ListView>
</Grid>
</Page>
In MainPage cs:
namespace App10
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
var list = new ObservableCollection<string>();
for (int i = 0; i < 10; i++)
{
list.Add("Item " + i);
}
listView.ItemsSource = list;
}
}
}
Sorry if I replicate, but I'm having a hard time wording this for a decent query.
I'm trying to filter a set of complex objects through an ICollectionView and then display each object that made it through, represented by one of their properties. In this particular example, the collection is a list of ComplexClass objects, each of which containing a Name and Number property. I wish to represent each in a ListBox, sitting within a Popup, by their Name.
Here's the XAML code for the MainWindow:
<Window
x:Name="mainWindow"
x:Class="TestWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:test="clr-namespace:TestWPF"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
</Window.Resources>
<Grid x:Name="mainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel x:Name="buttonStack">
<Button x:Name="popupButton" Click="popupButton_Click" Content="Pop-up!" VerticalAlignment="Top"/>
<Button x:Name="randomInsertButton" Click="randomInsertButton_Click" Content="Random Addition!"/>
</StackPanel>
<Popup x:Name="testPopup" PlacementTarget="{Binding ElementName=popupButton}" PopupAnimation="Scroll" Placement="Left"
AllowsTransparency="True" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid>
<Button x:Name="popupsButton" Content="Button" Width="75" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ListBox x:Name="testListBox" Height="100" DataContext="{Binding ElementName=mainWindow}" ItemsSource="{Binding Source=strings}" ScrollViewer.VerticalScrollBarVisibility="Visible" SelectionChanged="testListBox_Selected"/>
</Grid>
</Popup>
</Grid>
</Window>
The MainWindow's code-behind:
namespace TestWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
static List<ComplexClass> stringList = new List<ComplexClass>();
public ObservableCollection<ComplexClass> strings;
public MainWindow()
{
InitializeComponent();
stringList.Add(new ComplexClass("apple",0));
stringList.Add(new ComplexClass("bat",2));
stringList.Add(new ComplexClass("cattle",6));
stringList.Add(new ComplexClass("dogma",5));
strings = new ObservableCollection<ComplexClass>(stringList);
Binding binding = new Binding();
binding.Source = strings;
testListBox.SetBinding(ListBox.ItemsSourceProperty, binding);
}
private void popupButton_Click(object sender, RoutedEventArgs e)
{
ICollectionView view = CollectionViewSource.GetDefaultView(strings);
view.Filter =
//null;
(o) =>
{
return (o as ComplexClass).Name!=string.Empty;
};
view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Descending));
testPopup.IsOpen = !testPopup.IsOpen;
}
private void randomInsertButton_Click(object sender, RoutedEventArgs e)
{
Random r = new Random();
stringList.Add(stringList[r.Next(0, stringList.Count)]);
strings.Add(stringList.Last());
}
private void testListBox_Selected(object sender, RoutedEventArgs e)
{
ComplexClass selected =(ComplexClass)(sender as ListBox).SelectedItem;
stringList.Add(selected);
strings.Add(selected);
}
}
}
And finally, the ComplexClass code:
namespace TestWPF
{
public class ComplexClass
{
public string Name { get; private set; }
public int Number { get; private set; }
public ComplexClass(string name, int number)
{
Name = name;
Number = number;
}
}
}
What it's currently doing is displaying each of the objects as if they've been ToString()-ed: "TestWPF.ComplexClass".
I actually want them to be displayed as:
dogma
cat
bat
apple
in that order.
Can I get some help with this?
A couple of issues here.
First, you are binding to the list in your XAML:
<ListBox ... ItemsSource="{Binding Source=strings}" ... />
but then also setting the binding in code:
Binding binding = new Binding();
binding.Source = strings;
testListBox.SetBinding(ListBox.ItemsSourceProperty, binding);
You need to do one or the other: I'd suggest binding in XAML, and removing the binding stuff from the code-behind.
Second, the ComplexClass items are being ToString-ified because that's the default behaviour of a ListBoxItem when supplied with content (the items in a WPF ListBox are wrapped inside ListBoxItem elements). The easiest way for you to fix this is to set the DisplayMemberPath property on the ListBox:
<ListBox ... ItemsSource="{Binding Source=strings}" DisplayMemberPath="Name" ... />