bubble scroll event from FlowDocumentScrollViewer to ListView - c#

I have the following (part of) XAML:
<ListView x:Name="logView" Grid.Row="2" ItemsSource="{Binding Logs}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<FlowDocumentScrollViewer ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<FlowDocument FontSize="12" FontFamily="Calibri" PagePadding="0" TextAlignment="Left">
<Paragraph TextIndent="-10" Margin="10,0,0,0">
<Run Text="{Binding .}" />
</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Logs is an IEnumerable<string> that the ListView is bound to (via a ViewModel, but that shouldn't matter here).
If I remove the whole <ListView.ItemTemplate>...</ListView.ItemTemplate>, I have the mouse wheel scrolling behaviour I want. But with the FlowDocumentScrollViewer and its content, scrolling does not work as smoothly any more. It still scrolls, but just every now and then, most of the time it gets stuck.
Trying to solve this, I followed this solution and created a PreviewMouseWheel handler in the codebehind
private void BubbleScrollingToLogView(object sender, MouseWheelEventArgs e)
{
if (!e.Handled)
{
e.Handled = true;
var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
eventArg.RoutedEvent = MouseWheelEvent;
eventArg.Source = sender;
logView.RaiseEvent(eventArg);
}
}
and added it in the XAML:
....
<FlowDocumentScrollViewer ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
PreviewMouseWheel="BubbleScrollingToLogView">
....
But it did not make any change in behaviour. I even tried adding PreviewMouseWheel="BubbleScrollingToLogView" to <FlowDocument> and <Paragraph>, assuming that those might catch the event as well. But nothing helped.
So what do I need to do to get the smooth, default scrolling behaviour of the ListView?

If you decide to use only FlowDocumentScrollViewer you can bind Document property to your property on view model.
<FlowDocumentScrollViewer Document="{Binding Document}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
</FlowDocumentScrollViewer>
In your view model define Document property of FlowDocument type.
private FlowDocument document;
public FlowDocument Document
{
get { return document; }
set
{
document = value;
OnPropertyChanged();
}
}
Create FlowDocument from Logs:
var doc = new FlowDocument();
doc.FontSize = 12;
doc.FontFamily = new FontFamily("Calibri");
doc.PagePadding = new Thickness(0);
doc.TextAlignment = TextAlignment.Left;
foreach (var log in Logs)
{
var paragraph = new Paragraph(new Run(log));
paragraph.TextIndent = -10;
paragraph.Margin = new Thickness(10, 0, 0, 0);
doc.Blocks.Add(paragraph);
}
Document = doc;

It was enough to disable the FlowDocumentScrollViewer to get the default scrolling behaviour. I didn't notice any difference in the appearance due to disabling.
<FlowDocumentScrollViewer ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
IsEnabled="False">

Related

Binding Errors on collapsed Gui Elements

I have a lot of bindings in a grid which are collapsed or visible based on the selecteditem in a treeview tree. so based on the object, the correct view pops up and the other collapses.
But I get lots of binding errors of the gui elements in the collapsed views. that is so, because I have a "myselecteditem" in the view model that fits to the view which is opened, but not to the collapsed ones.
is my approach stupid? can i suppress bindings for gui elemnts in a collapsed grid?
Code (shorted for lazy readers):
XAML:
<Grid x:Name="G_G_Content" Grid.Column="1">
<Grid x:Name="G_G_Abrechnung_Control" Visibility="Collapsed">
[...]
</Grid>
<Grid x:Name="G_G_Mitglied_Aktion" Visibility="Collapsed">
[...]
</Grid>
<Grid x:Name="G_G_Mitglied_Aktion_Nachweis" Visibility="Visible">
[...]
</Grid>
</Grid>
<TreeView
x:Name="G_tv_explorer"
Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding TreeViewItemSource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItemChanged="G_tv_explorer_SelectedItemChanged"
TreeViewItem.Expanded="TreeViewItem_Expanded" />
C#:
private void G_tv_explorer_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
//set active treeviewitem
if (G_tv_explorer.SelectedItem != null)
{
//set treeview && ViMo
ViMo.TreeViewSelectedItem = e.NewValue;
RedrawGui();
ViMo.MySelectedItem = ((TreeViewItem)e.NewValue).Tag;
}
}
public void RedrawGui()
{
//redraw gui, dependant on selectedtreeviewitem
if (ViMo.TreeViewSelectedItem != null)
{
if(ViMo.TreeViewSelectedItem is string)
MessageBox.Show("Dummy");
else
{
//this is a placeholder
if (((TreeViewItem)ViMo.TreeViewSelectedItem).Header.ToString() == "Verwaltung")
{
G_G_Mitglied_Aktion_Nachweis.Visibility = System.Windows.Visibility.Collapsed;
G_G_Abrechnung_Control.Visibility = System.Windows.Visibility.Visible;
}
else if (((TreeViewItem)ViMo.TreeViewSelectedItem).Tag is MVVM.Model.Jahresabschluss)
{
G_G_Mitglied_Aktion_Nachweis.Visibility = System.Windows.Visibility.Collapsed;
G_G_Abrechnung.Visibility = System.Windows.Visibility.Visible;
}
}
}
}
Where RedrawGUI only sets the Grids Visible / collapsed based on the Object Type of the SelectedItem

UWP - get first child of Gridview

<GridView x:Name="MainGridStations" ItemsSource="{x:Bind Stations}" IsItemClickEnabled="True" ItemClick="GridView_ItemClick">
<GridView.ItemTemplate>
<DataTemplate x:DataType="local:Station">
<Grid x:Name="WantToSelectByCode">
<Grid Background="White" HorizontalAlignment="Center" Width="300" Height="200" VerticalAlignment="Center">
<Grid Background="#e4f0fc" Height="65" VerticalAlignment="Bottom" Opacity="0.8">
<TextBlock x:Name="StationName" Text="{Binding Name}" FontWeight="Bold" Foreground="#2c9a8b" HorizontalAlignment="Center" />
</Grid>
</Grid>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
I'm trying to select a child by index of a dynamically filled gridview but what i've tried always returns null.
Like so for the first child for example:
var container = MainGridStations.ContainerFromIndex(0);
var presenter = VisualTreeHelper.GetChild(container, 0) as GridViewItem;
What am I doing wrong here?
You can get the corresponding GridViewItem from the method ItemsControl.ContainerFromIndex(Int32) directly and don't need to use the VisualTreeHelper to get it again.
var container = MainGridStations.ContainerFromIndex(0);
GridViewItem gridViewItem= container as GridViewItem;
gridViewItem.Background = new SolidColorBrush(Colors.Red);
The container have been the corresponding GridViewItem got from the index.
Note that: since your inner Grid have a Background="White" property configuration, you can delete the code to see the effect more obviously using my above code to change the gridViewItem.Background.
---Update---
You must get the GridViewItem after the items loaded. You can try the code in your GridView_ItemClick event handler or the page's loaded event hander. Also pay attention to my above note that to get more obvious effect, please delete the Background="White" in your above xaml code.
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var container = MainGridStations.ContainerFromIndex(0);
GridViewItem gridViewItem = container as GridViewItem;
gridViewItem.Background = new SolidColorBrush(Colors.Green);
}
//get the item here
private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
var container = MainGridStations.ContainerFromIndex(0);
GridViewItem gridViewItem= container as GridViewItem;
gridViewItem.Background = new SolidColorBrush(Colors.Red);
//var presenter = VisualTreeHelper.GetChild(container, 0) as GridViewItem;
}

Modify the visibility of a row via keyboard interaction in UWP application

Here is my scenario. My table has fixed number of columns, say 2, and initially, it has only one visible row, but when the focus is on the last column of row 1 and the user press 'tab', row 2 will be made visible.
My problem is that I can't dynamically select the row I want to make visible because I have to specify its x:Name during compilation.
Below is my current work.
.xaml file
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" x:Name="SP1">
<TextBox Text="1-1"/>
<TextBox Text="1-2" KeyDown="showNextLine"/>
</StackPanel>
<StackPanel Orientation="Horizontal" x:Name="SP2" Visibility="Collapsed">
<TextBox Text="2-1"/>
<TextBox Text="2-2"/>
</StackPanel>
<!--the remaining rows...-->
</StackPanel>
.cs file
private int lastRowIndex = 1;
private void showNextLine(object sender, KeyRoutedEventArgs e)
{
lastRowIndex++;
string nextLineName = "SP" + lastRowIndex.ToString();
nextLineName.Visibility = Visibility.Visible; // which causes error because nextLineName is string instead of StackPanel
}
Besides, my current implementation is to create 50 rows and make the last 49 invisible initially, and I am open to any method to group all the TextBox more systematically or flexibly.
Thanks for reading.
You could give the parent StackPanel an x:Name or keep a reference to it if you create it dynamically:
<StackPanel x:Name="root" Orientation="Vertical">
<StackPanel Orientation="Horizontal" x:Name="SP1">
<TextBox Text="1-1"/>
<TextBox Text="1-2" KeyDown="showNextLine"/>
</StackPanel>
<StackPanel Orientation="Horizontal" x:Name="SP2" Visibility="Collapsed">
<TextBox Text="2-1"/>
<TextBox Text="2-2"/>
</StackPanel>
<!--the remaining rows...-->
</StackPanel>
...and then get a reference to a child StackPanel using the Children property and some basic LINQ:
private void showNextLine(object sender, KeyRoutedEventArgs e)
{
lastRowIndex++;
string nextLineName = "SP" + lastRowIndex.ToString();
StackPanel child = root.Children.OfType<StackPanel>().FirstOrDefault(x => x.Name == nextLineName);
if (child != null)
child.Visibility = Visibility.Visible;
}
How could I create a StackPanel dynamically?
Like this:
var sp = new StackPanel { Name = "SP3", Orientation = Orientation.Horizontal, Visibility = Visibility.Collapsed };
sp.Children.Add(new TextBlock { Text = "3-1" });
var txt = new TextBlock() { Text = "3-2" };
txt.KeyDown += showNextLine;
sp.Children.Add(txt);
root.Children.Add(sp);
I can't think of an easy way to do the first part of this (since you have 50 stack panels), but if you put all of them in a dictionary, then you could update them using just the key.
Here's the dictionary part done manually:
Dictionary<int, StackPanel> myStackPanels = new Dictionary<int, StackPanel>();
myStackPanels.Add(1, SP1);
myStackPanels.Add(2, SP2);
Then, here's what ShowNextLine would look like:
private void showNextLine(object sender, KeyRoutedEventArgs e)
{
lastRowIndex++;
// Modify the StackPanel whose key is lastRowIndex;
myStackPanels[lastRowIndex] = Visibility.Visible;
}

GridView, ItemTemplate, DataTemplate binding in C# code behind

I have following working XAML and C# code behind:
<Grid x:Name="MainGrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<GridView ItemsSource="{Binding}">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Height="100" Width="150">
<Grid.Background>
<SolidColorBrush Color="{Binding Color}"/>
</Grid.Background>
<StackPanel>
<StackPanel.Background>
<SolidColorBrush Color="{Binding Color}"/>
</StackPanel.Background>
<TextBlock FontSize="15" Margin="10" Text="{Binding Name}"/>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
CODE behind:
public MainPage()
{
this.InitializeComponent();
var _Colors = typeof(Colors)
.GetRuntimeProperties()
.Select(x => new
{
Color = (Color)x.GetValue(null),
Name = x.Name
});
this.DataContext = _Colors;
}
This works fine.
But I want to do all the XAML part in C# code behind. In XAML, only MainGrid will be there, all its child elements and bindings needs to be done in code behind.
I have tried something like this in MainPage_Loaded event:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
try
{
GridView gridView = new GridView()
{
ItemTemplate = new DataTemplate
{
//Don't know what to add here
}
};
Grid grid = new Grid();
Binding bindingObject = new Binding();
bindingObject.Source = this;
grid.SetBinding(Grid.BackgroundProperty, bindingObject);
//...
// Don't know how to add grid inside gridView in Code.
//...
MainGrid.Children.Add(gridView);
}
catch (Exception ex)
{
}
}
First of all I'd like to advise you not to create elements in code unless you have a real good reason to do so.
Regardless, considering that item templates are factory-like objects for controls (you 'spawn' a new set of controls for each item). You use the FrameworkElementFactory to model the subtree and then assign that the item template's VisualTree property.

How I can bind textblock with bolded and normal text

I have a part of text which some of the words are formatted.
These text are listed in a ListBox. When user clicks ListBoxitem, I want to collect that selectedItem and take user to the other place. My problem is that I cant bind TextBlock with another instance of TextBlock. And that TextBlock has many inlines, which I want to show.
I have been trying this solution:
<ListBox Width="800" Name="foundedTextBlocksListBox" SelectionChanged="foundedTextBlocksListBox_SelectionChanged" Background="Transparent" ItemsSource="{Binding}" Grid.Row="2" Visibility="Visible" Height="Auto" HorizontalAlignment="Center">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel VerticalAlignment="Center" Orientation="Vertical">
<TextBlock x:Name="foundedTextBlocks" DataContext="{Binding Textblock}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
After Binding to DataContext like this:
ObservableCollection<FoundedTextBlock> listOfFoundedTextBlockResults = new ObservableCollection<FoundedTextBlock>();
TextBlock textblock = new TextBlock();
while (blockString.IndexOf("<b>") != -1)
{
int startOfWord = blockString.IndexOf("<b>");
int endOfWord = blockString.IndexOf("</b>");
string text = blockString.Substring(0, startOfWord);
textblock.Inlines.Add(text);
string boldedWord = blockString.Substring(startOfWord + 3, endOfWord - startOfWord - 3);
textblock.Inlines.Add(new Run() { Text = boldedWord, FontWeight = FontWeights.Bold });
blockString = blockString.Substring(endOfWord + 4);
textblock.Inlines.Add(blockString);
}
textblock.Tag = dbInfo;
listOfFoundedTextBlockResults.Add(new FoundedTextBlock() { Textblock = textblock });
}
foundedTextBlocksListBox.DataContext = listOfFoundedTextBlockResults;
I can't see any ListBoxItems in ListBox. Is my Binding wrong or is this possible at all?
I managed before to get TextBlock.Text property to show but not the Inlines where are bolded text or any other Inlines after my first inline addition to TextBlock.
How I can solve this annoiyng problem? In short, I need to display many TextBlocks with formatted text...
FoundedTextBlock class has TextBlock textblock {get;set;} property
I'm saving to Tag property my class instance, so I could collect the information I need when SelectedValueChanged event occurs.
Maybe you should use a ContentPresenter instead of TextBlock in your XAML
Replace
<TextBlock x:Name="foundedTextBlocks" DataContext="{Binding Textblock}"></TextBlock>
with
<ContentPresenter Content="{Binding Textblock}" />
Please try it out ... the rest of your code is missing to provide a better answer.

Categories

Resources