|
Sorry, I misread the part that this was a context menu. A context menu doesn't sit in the visual tree so you can't relative source back to it. What you're looking to do will be some variation of this[^].
This space for rent
|
|
|
|
|
Looks like the button's Tag property is being set to the window's DataContext, and the context menu is getting its DataContext off the button's tag.
Interesting idea. Not sure how to apply this in my style.
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Following up on this.
I found a solution here.
Now my commands are firing. But the CommandParameters are all null.
Here's my XAML now:
<Style TargetType="TabItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}" >
<MenuItem Header="Class Tab"
Command="{Binding Source={StaticResource Proxy}, Path=Data.CloseTabCommand}"
CommandParameter="{Binding}"/>
<MenuItem Header="Class All Tabs"
Command="{Binding Source={StaticResource Proxy}, Path=Data.CloseAllTabsCommand}"
CommandParameter="{Binding}"/>
<MenuItem Header="Close All But This"
Command="{Binding Source={StaticResource Proxy}, Path=Data.CloseAllButThisTabCommand}"
CommandParameter="{Binding}"/>
</ContextMenu>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Grid Name="Panel">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="10,2"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Panel" Property="Background" Value="LightSkyBlue" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="Panel" Property="Background" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I tried using element name but still no luck. Any thoughts?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
|
I'm trying to create a listbox of hyperlinks. Here's the XAML:
<ListBox ItemsSource="{Binding QuickActions}"
x:Name="quickItemList">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Grid.Row="0"
Margin="5">
<Hyperlink x:Name="link">
<TextBlock Text="{Binding Caption}"/>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding ElementName=uc, Path=DataContext.QuickActionCommand}"
CommandParameter="{Binding ElementName=quickItemList, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Hyperlink>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and the VM code:
private ICommand _QuickActionCommand;
public ICommand QuickActionCommand
{
get
{
if (_QuickActionCommand == null)
_QuickActionCommand = new RelayCommand<object>(p => QuickActionExecuted(p), p => QuickActionCanExecute());
return _QuickActionCommand;
}
}
private bool QuickActionCanExecute()
{
return true;
}
private void QuickActionExecuted(QuickActionModel model)
{
}
When I click the link the parameter 'model' is null. The SelectedItem in the list is null. I know there's a way in the XAML to code this but I just can't get it right.
Anyone know how to do this?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Caveat - this is a guess
You are passing the list in the commandparameter can you pass the selected item instead.
However I would build it differently. Bind the selected item to the VM and the textbox click event uses the selected item to get the link, no need to pass parameters from the view in the click event.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Try:
CommandParameter="{Binding}"
Your command also looks a bit odd - how does a RelayCommand<object> convert the parameter from object to QuickActionModel ?
If it still doesn't work, check the output window in Visual Studio to see if there are any binding errors.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
|
|
I'm creating a control in WPF that looks like this
Right now I'm designing the Bay. It's really just 3 simple nested borders.
What I want is when the mouse is over ANY part of the control (any of the 3 borders), then show the red border.
The mouseOeverBorderStyle's Setters don't like the TargetName. This has to be fairly easy. Anyone know how to do this?
Thanks
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Unfortunately, the IsMouseOver property doesn't work unless you have a background. However, you can set the background to "Transparent":
<Style x:Key="mouseOverBorderStyle" TargetType="{x:Type Border}" BasedOn="{StaticResource borderStyle}">
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
You'll want to set the border thickness outside of the trigger; otherwise, the inner border shrinks slightly when you mouse over the control.
There's still a 2px white border around the edge which won't trigger the mouse-over behaviour. But I'm sure most people won't notice it.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Worked great. Thanks!
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
I have a problem with a DataGrid consuming a huge amount of memory when we have ~15,000 cells, each cell holding an edit control ie CheckBox, TextBox or ComboBox. The view takes 40 seconds to appear, and then consumes 1GB RAM. I can turn on virtualisation of rows and columns, which cures the slow start, and reduces memory by a factor of 3. However, that makes scrolling unacceptably slow ie many seconds.
I have a test app which allows me to test methods to improve performance. It displays values in a 2D matric, which is defined as a list of Row instance. Each Row object contains an array of node instances which hold the values.
The DataGrid is simple enough:
<DataGrid Grid.Row="2" Name="_dataGrid2"
ItemsSource="{Binding Matrix}"
AlternationCount="2"
AutoGenerateColumns="False"
Style="{StaticResource styleDataGridNoSelection}"
EnableRowVirtualization="True"
EnableColumnVirtualization="True"
VirtualizingStackPanel.IsVirtualizing="False"
VirtualizingStackPanel.VirtualizationMode="Recycling"
ScrollViewer.CanContentScroll="True"
HeadersVisibility="None"/>
The view constructor creates the column data templates:
public MainWindow()
{
InitializeComponent();
MainViewModel viewModel = new MainViewModel();
DataContext = viewModel;
_gridDataTemplateSelector = new GridDataTemplateSelector();
_gridDataTemplateSelector.LabelDataTemplate = Resources["LabelDataTemplate"] as DataTemplate;
_gridDataTemplateSelector.TextBoxDataTemplate = Resources["TextBoxDataTemplate"] as DataTemplate;
_gridDataTemplateSelector.CheckBoxDataTemplate = Resources["CheckBoxDataTemplate"] as DataTemplate;
_gridDataTemplateSelector.ComboBoxDataTemplate = Resources["ComboBoxDataTemplate"] as DataTemplate;
for (int i = 0; i < Row.constColumnCount; i++)
{
DataGridTemplateColumn col = new DataGridTemplateColumn();
FrameworkElementFactory fef = new FrameworkElementFactory(typeof(ContentPresenter));
Binding binding = new Binding();
fef.SetBinding(ContentPresenter.ContentProperty, binding);
fef.SetValue(ContentPresenter.ContentTemplateSelectorProperty, _gridDataTemplateSelector);
binding.Path = new PropertyPath("Values[" + i + "]");
DataTemplate dataTemplate = new DataTemplate();
dataTemplate.VisualTree = fef;
col.CellTemplate = dataTemplate;
_dataGrid2.Columns.Add(col);
}
The view model is also simple:
public class MainViewModel : System.ComponentModel.INotifyPropertyChanged
{
public MainViewModel()
{
const int RowCount = 150;
_matrix = new Row[RowCount];
for (int i = 0; i < RowCount; i++)
{
_matrix[i] = new Row("Row" + i.ToString(), i);
}
}
private Row[] _matrix;
public Row[] Matrix
{
get
{
return _matrix;
}
}
#region INotifyPropertyChanged
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
public virtual void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
And the row is just a simple array of nodes:
public class Row
{
private string name;
private Model.INode[] _values;
public string Name { get { return name; } }
public Model.INode[] Values { get { return _values; } }
public int Index { get; set; }
public const int constColumnCount = 90;
public Row(string name, int index)
{
Index = index;
this.name = name;
_values = new Model.INode[constColumnCount];
for (int i = 0; i < constColumnCount; i++)
{
if (index == 0)
{
_values[i] = new Model.LabelNode();
(_values[i] as Model.LabelNode).Value = i.ToString();
}
else
{
if (i == 0)
{
_values[i] = new Model.LabelNode();
(_values[i] as Model.LabelNode).Value = index.ToString();
}
else
{
switch ((i + index) % 3)
{
case 0:
_values[i] = new Model.TextNode();
(_values[i] as Model.TextNode).Value = i.ToString();
break;
case 1:
_values[i] = new Model.CheckBoxNode();
(_values[i] as Model.CheckBoxNode).Value = true;
break;
case 2:
_values[i] = new Model.ComboBoxNode();
(_values[i] as Model.ComboBoxNode).Values = new List<string>(2) { (i + index).ToString(), (i + index + 1).ToString() };
(_values[i] as Model.ComboBoxNode).Value = (_values[i] as Model.ComboBoxNode).Values[0];
break;
}
}
}
}
} }
The node classes are basic shells eg:
class TextNode : INode
{
public string Value { get; set; }
}
|
|
|
|
|
For your DataGrid, set this
ScrollViewer.IsDeferredScrollingEnabled="True"
This space for rent
|
|
|
|
|
Thanks. Sadly that doesn't really improve things, still slow.
|
|
|
|
|
Displaying a "collection of UI CONTROLS" in a data grid is probably the worst thing you can do with a data grid for a "large collection".
You're supposed to bind data grid "cell types" to a "DATA collection" if you want to "virtualize" (the UI).
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
Thanks. Can you expand on your answer? I don't know what you mean by data grid "cell types".
I assume you are saying that I should not create an instance of a UI control for each data item. Instead I should have a minimal set of UI controls, and change the data each displays. Clearly for a uniform grid where all controls are the same, or all cells in a given column are the same, that is easy, and it can be done in the view model. However, I might have one row of check boxes, followed by several rows of combo boxes, followed by several rows of hex number edit boxes etc and this can be scrolled vertically.
|
|
|
|
|
You need to be more specific than "rows of checkboxes and combo boxes";
I've used "list views" to display "user controls" that contained "rows".
I said before that there was more than one way to get a "grid effect"; your description of "why" and from "what" is non-existent so it is hard to make more concrete suggestions.
ListView basics and virtualization concepts – Blog Alain Zanchetta
Bottom line: no "data binding", no virtualization.
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
modified 15-Jun-18 14:47pm.
|
|
|
|
|
Some requirements were given in my previous question along with example code, that you replied to, however it was a long post.
We need to display tabular data, in a table with rows and columns. A given data value can be a Boolean, or a combo box selection, or a numeric value (signed or unsigned). In general each value is editable. The table of settings is to be displayed in a WPF view and hence the size will depend on the view size. Clearly not all of the items will be visible to the user as the table is so large, so there will be vertical and horizontal scroll bars.
I had hoped that the DataGrid implemented virtualisation well enough to reduce the overhead, but it appears that WPF is very very memory intensive. When I turn on virtualisation for both columns and rows, the performance is unacceptable (slow).
I haven't tried the list view. I guess I could define a list view item as a GRID control with one row, and multiple columns and use the
SharedSizeGroup property to create columns, assuming that would work, but I can't see why it would reduce the massive memory overhead.
A possible solution I am working on is to use a DataGrid with each item being a Label control. That massively reduces the memory usage. Double clicking on an item makes it editable, displaying the appropriate edit control e.g. CheckBox.
|
|
|
|
|
You still haven't provided any sense of the logic or purpose for the "UI".
All data has some sort of "structure" that drives the design.
Nothing you've described justifies your reasoning about the "technical" details.
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
Why not change your UI design to a more sensible layout. List all the setting values in a grid but make the user edit them in a dialog box. The user should have to double click on the grid row he wants to edit, pop a dialog with the data from that row, allow the user to edit the data of the single data row in discreet controls, save the data back to the underlying collection.
The design eliminates the need to editable controls in the data grid and will simplify your life enormously.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Thanks. I might well use that approach, depending on how much progress I make.
Currently I am implementing a solution whereby the grid is read only but when the user double clicks on a cell, it becomes editable. So instead of plain text, they now see a combo box, or a tick box, or a numeric value editor. Double click elsewhere, and the cell becomes read only. This works well with the Microsoft WPF DataGrid though the memory usage is still rather high. The non editable cell control is a Label.
I am also looking at the WPF Table View control on CodePlex. As yet I cannot find out how to make the current cell active when double clicked on, but the memory usage and speed are better. Clearly I can easily pop up a dialog to edit a cell, or a row as you suggest since I can trap the mouse double click.
From searching around it does seem that the performance of grids is a widespread problem.
|
|
|
|
|
Don't use a Label if you can help it. Use a TextBlock instead. A Label is, relatively speaking, a much heavier weight control than a TextBlock.
This space for rent
|
|
|
|
|
Thanks, that's very helpful.
|
|
|
|
|
You're welcome.
This space for rent
|
|
|
|