Use ScrollBar in DropDownButton? - c#

I want use a scrollbar like ComboBox in my DropDown button, the structure actually is this:
<Controls:DropDownButton Content="Nazioni" Width="120" Margin="0, 0, 20, 0"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.CanContentScroll="True"
ItemsSource="{Binding Countries}"
ItemTemplate="{StaticResource CombinedTemplate}"/>
but I don't see any ScrollViewer as you can see in the image below:

The drop-down of the DropDownButton already contains a ScrollViewer (it is named "SubMenuScrollViewer"), so scrolling through its items is supported out-of-the-box. The thing is, that particular ScrollViewer is styled differently than a default ScrollViewer - assuming we're talking about vertical scrolling, it has two buttons above and below the list, responsible for scrolling up and down respectively, as shown below:
So your best bet is to make that particular ScrollViewer use the default style rather than a custom one. By inspecting the MahApps.Metro source code we can see that the ScrollViewer in question is wired to use a dynamic resource with a key value of {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}. So what you need to do is to supply a default style with that key for that control:
<Controls:DropDownButton (...)>
<Controls:DropDownButton.Resources>
<Style x:Key="{ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}"
TargetType="{x:Type ScrollViewer}"
BasedOn="{StaticResource {x:Type ScrollViewer}}" />
</Controls.DropDownButton.Resources>
</Controls.DropDownButton>
This way the ScrollViewer within the drop-down will be styled with a default style shipped with MahApps.Metro.

To be certain that scrolling will behave as expected, you cannot rely on WPF to place a ScrollViewer where it should be.
As any content can be place on the dropdown, your best option is to drop a ScrollViewer straight onto the component.
This way, you can explicitly name it, and have access to its properties.
If you bind your list of countries to the lstContent box, you do away with all the messing.
<extToolkit:DropDownButton Content="Click Me" Margin="15" >
<extToolkit:DropDownButton.DropDownContent>
<ScrollViewer>
<ListBox Name="lstContent" ItemsSource="{Binding Countries}" ItemTemplate="{StaticResource CombinedTemplate}"/>
</ScrollViewer>
</extToolkit:DropDownButton.DropDownContent>
</extToolkit:DropDownButton>

Related

Need to modfy the default tab order of child elements

I have custom control like below. When press tab key focus will move in the order elements arrangement.
Query:
When stackpanel receive tab focus I need to change default tab order toggle button present in stackpanel
Default Tab Order:
DockPanel--Border---StackPanel-->Button1-->button2-->button3
Expected Order
DockPanel--Border---StackPanel-->Button3-->button2-->button1
I need update TabOrder based on its parent. Please suggestion solution modify the tab order based on parent
Note: I need UI as like below arrangements, only i need to modify the tab order for buttons
<DockPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Border x:Name="MainBorder">
<StackPanel>
<ToggleButton>Button 1</ToggleButton>
<ToggleButton>Button 3</ToggleButton>
<ToggleButton>Button 3</ToggleButton>
</StackPanel>
</Border>
</DockPanel>
As mentioned in comments do set the TabIndex property. To step within control do use KeyboardNavigation.TabNavigation attached property.
<DockPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Border x:Name="MainBorder">
<StackPanel KeyboardNavigation.TabNavigation="Local">
<ToggleButton KeyboardNavigation.TabIndex="3">Button 1</ToggleButton>
<ToggleButton KeyboardNavigation.TabIndex="2">Button 2</ToggleButton>
<ToggleButton KeyboardNavigation.TabIndex="1">Button 3</ToggleButton>
</StackPanel>
</Border>
</DockPanel>
If you want to modify the tab order at run time I would advice you to create a behavior for it. See Use of Behavior in WPF MVVM? To access attached property from code see Get and set WPF custom attached property from code behind

ListView ignoring drag and drop when setting a ItemTemplate

I have set up a ListView to manage tabs on my app. So users can drag and drop tabs to new instances of the app and reorder tabs. All goes great until I set up a ControlTemplate for the Itens at the ListView.
I used ControlTemplate because when I add a ListView.ItemTemplate directly it won`t work (it does not affect the listview item). So before adding a new item I use this:
tab.Template = (ControlTemplate)this.Resources["listViewItemTemplate"];
So the tabs is looking great, but all the functions that used to work (like drag and drop) isn't working anymore. When I remove the ControlTemplate the ListView show only one little string.
This is what is happening
My ListView Code:
<ListView x:Name="TabsListView"
CanDragItems="True"
AllowDrop="True"
DragEnter="TabsListView_DragEnter"
Drop="TabsListView_Drop"
ScrollViewer.VerticalScrollBarVisibility="Disabled
CanReorderItems="True"
DragItemsStarting="TabsListView_DragItemsStarting"
ItemClick="TabsListView_ItemClick" >
I know for sure that the issue isn't on the drag and drop method.
To create a new item I just use Add. Of course I checked every single item inside ControlTemplate to know that it is not blocking anything.
The control template is:
<ControlTemplate x:Key="listViewItemTemplate" >
<ListViewItem>
<Grid Tapped="Grid_Tapped" Width="180" Margin="-12,-12,-12,0">
<TextBlock Text="{Binding Name}" Margin="5,6,0,0"></TextBlock>
</Grid>
</ListViewItem>
</ControlTemplate>
The reason is that you are using ControlTemplate and embedding a ListViewItem in there. ListViewItems are created automatically by the control to contain the items, and ItemTemplate is a template of the contents of this container. So it should look like this:
<DataTemplate x:Key="listViewItemTemplate" >
<Grid Tapped="Grid_Tapped" Width="180" Margin="-12,-12,-12,0">
<TextBlock Text="{Binding Name}" Margin="5,6,0,0"></TextBlock>
</Grid>
</ControlTemplate>
<ListView ...
ItemTemplate="{ThemeResource listViewItemTemplate}">
...
</ListView>
Furthermore, if you want to style the ListViewItem (the container), you can create a style with TargetType="ListViewItem" and set it as ListView.ItemContainerStyle.
<Style TargetType="ListViewItem" x:Key="TabListViewItemContainerStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Grid x:Name="ContentBorder">
... some template with ContentPresenter:
<ContentPresenter x:Name="ContentPresenter" ... />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ListView ...
ItemContainerStyle="{StaticResource TabListViewItemContainerStyle}">
...
</ListView>
You can find the default style in: C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.15063.0\Generic
Update
I have checked the code and found the problem. What you are actually doing is adding ListViewItems directly in code:
ListViewItem tab = new ListViewItem();
DataModel model = new DataModel();
model.Name = $"NewTab {counter}";
tab.DataContext = model;
tab.Content = model.ToString();
TabsListView.Items.Add(tab);
Unfortunately this does not work, because when ListView sees an item that is of type ListViewItem, it just adds it to the list, but ItemTemplate doesn't apply to it, because that is applied only when the items are not UIElement but rather a custom class. If you use DataModel directly, ItemTemplate will be applied as expected:
DataModel model = new DataModel();
model.Name = $"NewTab {counter}";
TabsListView.Items.Add(model);
ListView encounters DataModel, knows that just a class not a UIElement, so it internally creates a ListViewItem, applies ItemContainerStyle to it and then uses the ItemTemplate to create the UI that will be displayed inside the ListViewItem's ContentPresenter. It is better to use this approach as it gives you better decoupling as you don't have to create UI-based classes in the code-behind and you get more control (as you can change both the style of the item as well as the container).

Forcing a ListBox to re-render

Background:
I have a ListBox containing items defined by DataTemplates. Right now, if an object in the list has the property IsEditable set to true, the item's property information will be displayed inside of textboxes (via DataTemplate change), instead of textblocks (so the user can edit the content of that list item)
IsEditable is toggled on/off by a button inside of each list item. I have been told that we need to keep the state of all objects consistent, which means I can't just rebind the ItemsSource and lose everything.
Currently, I'm using this to re-render:
this.lbPoints.Dispatcher.Invoke(DispatcherPriority.Render, new Action(() => { }));
Question:
The aforementioned code snippet KIND OF does its job. By "kind of", I mean, it does eventually cause my data to become re-rendered, but only when I scroll to the bottom of the list and then scroll back up to the item i'm trying to re-render.
1) How can I re-render the data immediately without having to scroll around to get it to show up?
The guys commenting are right that you're going about this the wrong way... there is rarely a need to force a ListBox to re-render. You're probably causing yourself some additional grief trying to switch the DataTemplates (although it is possible). Instead of that, think about data binding the TextBox.IsReadOnly property to your IsEditable property:
<TextBox IsReadOnly="{Binding IsEditable}" Text="{Binding Text}" />
Another alternative is to use a BooleanToVisibilityConverter to show a different Grid in your DataTemplate when your IsEditable property is true. Unfortunately, that Converter doesn't have an inverse operation, so you could create an IsNotEditing property to bind to the Grid in the DataTemplate that is originally displayed. I'm not sure if that's clear... see this example:
<DataTemplate DataType="{x:Type YourPrefix:YourDataType}">
<Grid>
<Grid Visibility="{Binding IsNotEditing, Converter={StaticResource
BooleanToVisibilityConverter}}">
<!-- Define your uneditable UI here -->
</Grid>
<Grid Visibility="{Binding IsEditing, Converter={StaticResource
BooleanToVisibilityConverter}}">
<!-- Define your editable UI here -->
</Grid>
</Grid>
</DataTemplate>
You could also define your own BooleanToVisibilityConverter class that has an IsInverted property, so that you can just use the one IsEditing property. You'd need to declare two Converters still, like this:
<Converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<Converters:BoolToVisibilityConverter x:Key="InvertedBoolToVisibilityConverter"
IsInverted="True" />
Then your XAML would be like this:
<Grid Visibility="{Binding IsEditing, Converter={StaticResource
InvertedBoolToVisibilityConverter}}">
<!-- Define your uneditable UI here -->
</Grid>
<Grid Visibility="{Binding IsEditing, Converter={StaticResource
BoolToVisibilityConverter}}">
<!-- Define your editable UI here -->
</Grid>

Attach attribute to Scrollviewer of Textbox

I need to sync two scroll viewers in WPF, one of which is part of the TextBox item.
I intend on using the method described on CodeProject here:
However, this requires that I can attach attributes to both scrollviewers. One of the scrollviewers is the scrollviewer that comes as part of a textbox.
Code at the moment:
<ScrollViewer Core:ScrollSynchronizer.VerticalScrollGroup="V1">
<UIComponents:LineNumberBox
x:Name="LineBox"
VisibleLines="{Binding ElementName=CodeBox, Path=(UIComponents:VisibleLinesBinder.VisibleLines)}"
Padding="2,10,2,0"
FontSize="14"
Grid.Column="0"
/>
</ScrollViewer>
<UIComponents:SourceCodeBox
x:Name="CodeBox"
Padding="10"
FontSize="14"
Grid.Column="1"
UIComponents:VisibleLinesBinder.ObserveVisibleLines="True"
Core:ScrollSynchronizer.VerticalScrollGroup="V1"
/>
A UIComponents:SourceCodeBox is just a wrapper around a normal WPF Textbox.
Obviously the Core:ScrollSynchronizer.VerticalScrollGroup="V1" on the SourceCodeBox doesnt work? So how would I attach that attribute to the ScrollViewer within it? C# or XAML methods are both fine.
In case it makes any difference this is part of a User Control I am developing.
put the attatched behavior in a style like this:
<UIComponents:SourceCodeBox
x:Name="CodeBox"
Padding="10"
FontSize="14"
Grid.Column="1"
UIComponents:VisibleLinesBinder.ObserveVisibleLines="True"
Core:ScrollSynchronizer.VerticalScrollGroup="V1">
<UIComponents:SourceCodeBox.Resources>
<Style TargetType="ScrollViewer">
<Setter Property="Core:ScrollSynchronizer.VerticalScrollGroup" Value="V1"/>
</Style>
</UIComponents:SourceCodeBox.Resources>
</UIComponents:SourceCodeBox>

hide or remove border of wpf button in C# code

I have a stackpanel named "mystack" in my xaml file and I am adding buttons in it dynamically from the .cs file and want to remove the border of buttons in C# .cs file
what I really want is to populate this stackpanel with the buttons coming from a list of string
thanks in advance
xaml:
<Grid HorizontalAlignment="Left" Height="227" Margin="10,10,0,0" Grid.Row="2"
VerticalAlignment="Top" Width="530">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Name="mystack" HorizontalAlignment="Left" Grid.Row="2"
VerticalAlignment="Top" Width="520"/>
</ScrollViewer>
</Grid>
.cs:
public List<String> Schools()
{
List<String> l = new List<string>();
l.Add("SST");
l.Add("SBE");
l.Add("SSH");
return l;
}
I agree with HighCore, you generally do not want to manipulate the UI elements in your code.
To remove the Border of the buttons you can set a Button's BorderThickness property to "0" in Xaml or to new Thickness(0) in the code-behind.
i.e.
myButton.BorderThickness = new Thickness(0);
EDIT:
Okay, I noticed your updated question. I would create a property that stores your list of schools and bind to it in a way similar to this:
public List<string> Schools
{
get { return _schools; }
set { _schools = value; }
}
Somewhere you need to set the DataContext of the control to your class containing the Schools property. If you are dynamically updating the list of Schools you'll need to implement INotifyPropertyChanged so the UI knows when to update. And then your Xaml would look something like this:
<ItemsControl ItemsSource="{Binding Schools}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" BorderThickness="0" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl>
You can't remove button's border like: btn.BorderThicknes=new Thickness(0);
See this: Removing button's border
The fast Fix:
What I had to do to effectively hide the button border - and due to the button control template I believe which utilizes and changes Button border (i.e. even if you remove it it'd draw it on some trigger I believe)...
...was to set BorderBrush="Transparent" as well (I always do BorderThickness as well but I'm guessing it's not needed - only for visual/layout look'n'feel)
i.e. setting thickness alone is not enough.
I'm really not sure that's the bets way to do it, or actually I'm
quite sure there must be something smarter - but I always end up with
that.
The Right Way:
Proper way - and recommended - is to write your own Button template -
based on the Microsoft official one - or base it on it - and do what
you need w/o borders.
For the code behind/C#:
You really don't need that as per your changed question - do what others suggested already
the best way to do this is :
<Style TargetType="Button">
<Style.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="0"/>
</Style>
</Style.Resources>
</Style>
what I really want is to populate this stackpanel with the buttons
coming from a list of string
That's called a ListBox:
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" BorderThickness="0"/>
<!-- Whatever other customizations to the button -->
</DataTemplate
</ListBox.ItemTemplate>
</ListBox>
ViewModel:
public class ViewModel
{
public ObservableCollection<string> Items {get;set;}
public ViewModel()
{
Items = new ObservablecCollection<string>();
Items.Add("String1");
Items.Add("String2");
Items.Add("String3");
}
}
You need to learn the MVVM pattern.

Categories

Resources