|
The subject really say it all, even if it sounds over-complicated. Please hang on.
I have a DataGrid. There are several columns in the DataGrid.
One of the columns contains a ListView. The listView holds several rows.
Each row contains a TextBlock and a Button.
The TextBlock's Text property contains a name, which I want to pass to my ViewModel.
I have managed to create a small example of this which should be just to "copy-paste-run".
Since it is "a lot" of code I have made a public gist to store it in.
Link to source in GIT Gist[^]
Setup:
* I use Visual Studio 2015 and compiling to NET 4.5.2.
* I also use Fody.PropertyChanged, then I'm not needed to manually type all stuff required for the INotifyPropertyChanged interface.
* Create a new WPF application and call it: WpfTest.
* Save the project.
* Install the Fody.Propertychanged in the NuGet Package Manager.
* Create the FodyWeavers.xml file.
* Paste all code from the Gist.
Note:
If the compiler says: "Fody: Could not find a weaver named 'PropertyChanged'. ..."
Remove the <PropertyChanged /> from the FodyWeaver.xml file and try again. That line is not neccessary for this and I don't understand why it sometimes fail when compiling.
This is what I want
If you click any of the buttons in the DataGrid I want the associated name on the same row in the ListView to be bound to the SelectedName property in the ViewModel.
I have no clue how to do the binding of the selected Textblock's Text property to the ViewModel in the View's xaml code.
For example: Press the button to the right of NameA. I then want my ViewModels SelectedName to be "NameA".
I tried this in the ListView, but no luck:
<ListView ItemsSource="{Binding Names}" SelectedItem="{Binding SelectedName}">
I also tried this, with no luck:
<ListView ItemsSource="{Binding Names}" SelectedItem="{Binding Path=DataContext.SelectedName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}">
Please, how can I do this?
P.S
I can kinda get this to work by binding the SelectedItem property from the DataGrid and the SelectedIndex property from the ListView. But that feels like a work-around.
|
|
|
|
|
No sympathy.
- Add a click handler / delegate that "stuffs" the "selection"; i.e. "code behind". (It's PLUMBING).
- And you don't need all that "property changed stuff". If you know you are refreshing all controls on a container, you can call propertychanged with "no name" (on the container) and refresh all with one statement. You need a really "slow" machine for individual "property changed" events to make a difference.
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
Hi everybody,
I would like to set a background image to a Groupbox and have it displayed at run time.
Below are the lines inserted in the xaml file at the appropriate locations:
<Window.Resources>
<VisualBrush x:Key="myNewBrush">
<VisualBrush.Visual>
<Grid>
<Rectangle Fill="Transparent"/>
<Image Source = "/Resources/Images/BartMap.png"/>
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</Windows.Resources>
<GroupBox Grid.Row="1" Grid.Column="0" Background="Transparent">
<GroupBox.Header>
<Label FontWeight="Bold">Track plan </Label>
</GroupBox.Header>
<Grid Background="{StaticResource myNewBrush}">
</Grid>
</GroupBox>
My problem: The image background is not displayed at runtime.
Could anyone help me with this issue?
Best regards,
|
|
|
|
|
I copied your code into a project and ran it successfully. The only thing that's different in my version is that I have a different image (obviously). Check that the image is set to a Build Action of Resource and that Copy To Output Directory is set to Do Not Copy. My successful code:
<Window x:Class="WpfSample.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:local="clr-namespace:WpfSample"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<VisualBrush x:Key="myNewBrush">
<VisualBrush.Visual>
<Grid>
<Rectangle Fill="Transparent"/>
<Image Source = "real-sense-images.png" />
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</Window.Resources>
<Grid>
<GroupBox Background="Transparent">
<GroupBox.Header>
<Label FontWeight="Bold">Track plan</Label>
</GroupBox.Header>
<Grid Background="{StaticResource myNewBrush}"/>
</GroupBox>
</Grid>
</Window>
This space for rent
|
|
|
|
|
Hi Pete,
Thank you for your reply.
I'm wondering if you could precise a little bit what you mean by: "<i>Check that the image is set to a Build Action of Resource and that Copy To Output Directory is set to Do Not Copy</i>".
Best regards,
Hervend
|
|
|
|
|
I've checked and the two settings have the default values you specified.
Up to now I don't understand what's going on.
Hervend
|
|
|
|
|
Thank you all for your replies.
I have set the property Copy To Output Directory to Always Copy.
And everything seems now to be in order.
thank you for your support.
Hervend
|
|
|
|
|
Would you not be better off creating a style element:
<GroupBox Grid.Row="4" Header="Test" BorderBrush="Black" BorderThickness="1">
<GroupBox.Style>
<Style TargetType="GroupBox">
<Setter Property="Background" Value="{StaticResource myNewBrush}"/>
</Style>
</GroupBox.Style>
<Button Grid.Row="0" Name="_buttonPressMe" Content="Press me!" Click="_buttonPressMe_Click" Width="80"/>
</GroupBox>
Obviously the layout in the above is rubbish, but the background is set correctly. I tested this in a demo app and it ran fine.
|
|
|
|
|
Or like this:
<GroupBox Grid.Row="4" Header="Test" BorderBrush="Black" BorderThickness="1" Background="{StaticResource myNewBrush}">
<Button Grid.Row="0" Name="_buttonPressMe" Content="Press me!" Click="_buttonPressMe_Click" Width="80"/>
</GroupBox>
|
|
|
|
|
Thank you for your reply Leif,
But it doesn't seem to work neither. unfortunately ...
Hervend
|
|
|
|
|
Then as Pete suggests, it must be your image and its settings, or the way you specified your image in the xaml.
|
|
|
|
|
For those familiar with them, TVPs are a great way to reduce the number of calls to the database and improve performance. I use them to great effect in the MVC web app using the general technique below. In the stored procedure I use a cursor to iterate through the rows and do all the db stuff in one whack.
I copied the same routines to a WPF app I'm doing, however, and it flames out calling the proc. I've since deleted it and gone the multiple calls route so I don't remember the precise error message but in general it was barking about not liking the parameters.
I'm on the same box, same version of VS, etc. and literally copied and pasted my routines. Works in MVC, no joy in WPF.
Have any of you successfully used table value parameters in a WPF app, and if so, is there a trick I'm missing?
DataTable tvp = new DataTable();
tvp.Columns.Add(new DataColumn("Id", Type.GetType("System.Int32")));
tvp.Columns.Add(new DataColumn("RowIdx", Type.GetType("System.Int32")));
tvp.Columns.Add(new DataColumn("ParentId", Type.GetType("System.Int32")));
tvp.Columns.Add(new DataColumn("MemberId", Type.GetType("System.Int32")));
DataRow row = null;
int iRowIdx = 0;
foreach (int iId in Ids)
{
row = tvp.NewRow();
row["Id"] = 0;
row["RowIdx"] = iRowIdx++;
row["ParentId"] = iListId;
row["MemberId"] = iId;
tvp.Rows.Add(row);
}
...
Command.Parameters.AddWithValue("@Entries", tvp);
...
ExecuteNonQuery(), etc.
Thanks!
|
|
|
|
|
ADO.NET is ADO.NET; there's no difference whether you're calling it from ASP.NET, WPF, or a console application.
If it works in your ASP.NET application, but not in your WPF application, then there's a difference in the code which you haven't shown. For example, verify that Command is a SqlCommand instance, and not some other type.
NB: You can replace your Type.GetType calls with the typeof keyword[^]:
tvp.Columns.Add(new DataColumn("Id", typeof(int)));
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Hey, Richard.
Appreciate the feedback. Yeah, that was my take on it as well - ADO.NET should be ADO.NET. However, after a few decades of dealing with MS technologies, I never take that as gospel.
When I get some time I'm going to try it again as the performance difference is significant, but I eyeballed the code pretty intensely and am as sure as I ever am about such things that it was the same in both environments. I hope to be wrong about that because otherwise I'm out of ideas.
Do you use TVPs in your ADO.NET apps?
|
|
|
|
|
Christopher Duncan wrote: Do you use TVPs in your ADO.NET apps?
Yes, although I tend to use the IEnumerable<SqlDataRecord> approach, rather than a DataTable .
private static IEnumerable<SqlDataRecord> AsTableValuedParameter(IEnumerable<string> value)
{
var record = new SqlDataRecord(new SqlMetaData("Value", SqlDbType.NVarChar, 0x100));
foreach (string item in value)
{
record.SetValue(0, item);
yield return record;
}
}
...
IEnumerable<string> roleNames = ...;
var parameter = command.Parameters.AddWithValue("@RoleNames", AsTableValuedParameter(roleNames));
parameter.SqlDbType = SqlDbType.Structured;
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
That's a nice approach. The DataTable was from an example I found on TVPs. I was focused on making the db interaction worked and never thought to convert it to something more elegant. Deadlines and all that.
I'm going to take another swing at this today. Hopefully I just fat fingered something when I cloned the MVC code I was using. Appreciate all the help.
|
|
|
|
|
Well, apparently the fingers are indeed fat. Tried again this morning and it worked without a fuss.
Still using DataTable approach at this point. MS says SqlDataRecord is resource abusive and recommends not creating a new one but reusing a single instance. That aside, are there any benefits to the enumerable approach over populating a data table?
|
|
|
|
|
Christopher Duncan wrote: MS says SqlDataRecord is resource abusive and recommends not creating a new one but reusing a single instance. Which is why I was doing that in the sample I posted.
Christopher Duncan wrote: are there any benefits to the enumerable approach over populating a data table? Not that I'm aware of.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
My bad. Looked like you were allocating a new SqlDataRecord each time but I'm in the US and thus still trying to get my eyes to focus this morning. Need. More. Coffee.
|
|
|
|
|
I have a grid with two expanders in it. See this and this
When I resize the window and make it smaller, the tree's scrollbar does not come on. See this
When I set the Row Definitions to
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
I get this.
When I resize the window smaller the tree resizes correctly so the scrollbar now shows. Here
The problem is that now the expanders are separated instead of stacked.
Here is the XAML - It's a lot, so bear with me I'm desperate!!
<Grid Grid.Row="2"
Grid.Column="0"
Margin="3"
MinWidth="225"
Visibility="{Binding Path=IsUserLoggedIn, Converter={StaticResource BoolToVisConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!SEARCH>
<Grid Grid.Row="0"
Grid.Column="0"
Background="{StaticResource brushWatermarkBackground}"
Style="{StaticResource searchFieldStyle}"
Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Margin="5,2"
Text="Click here to search "
Foreground="{StaticResource brushWatermarkForeground}"
FontStyle="Italic"
FontSize="14">
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource TextInputToVisibilityConverter}">
<Binding ElementName="txtUserEntry2"
Path="Text.IsEmpty" />
<Binding ElementName="txtUserEntry2"
Path="IsFocused" />
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
<TextBox Grid.Column="0"
Name="txtUserEntry2"
Background="Transparent"
Text="{Binding SearchValue}"
FontSize="14"
HorizontalAlignment="Stretch">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding PeformSearchCommand}"/>
<KeyBinding Key="Tab" Command="{Binding PeformSearchCommand}"/>
</TextBox.InputBindings>
</TextBox>
<Button Grid.Column="1"
Height="22"
Width="22"
Margin="2"
ToolTip="Clear Search"
Command="{Binding ClearSearchCommand}">
<Image Source="/Jayhawk.UI.WPF;component/Media/Images/clear.png"/>
</Button>
</Grid>
<!PROJECTS>
<Expander Grid.Row="1"
Header="Projects"
Margin="0,0,0,2"
ScrollViewer.VerticalScrollBarVisibility="Visible"
IsExpanded="{Binding AreProjectsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<Grid VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!FILTERS>
<ComboBox Grid.Row="0"
ItemsSource="{Binding ProjectFilters}"
SelectedItem="{Binding SelectedProjectFilter}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}"
Margin="5" />
<TextBlock Text="{Binding Caption}"
Margin="5" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!SORT OPTIONS>
<ComboBox Grid.Row="1"
ItemsSource="{Binding ProjectSortOptions}"
SelectedItem="{Binding SelectedProjectSortOption}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Caption}"
Margin="5" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!PROJECT LIST>
<TreeView Grid.Row="2"
ItemsSource="{Binding Projects}"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding Expanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
<!PROGRESS INDICATOR>
<Grid Grid.Row="2"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Visibility="{Binding ProjectProgressVisible}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<progress:SpinningProgress Grid.Row="0"
Height="54"
Width="65"
Margin="5"/>
<TextBlock Grid.Row="1"
Text="Loading..."
HorizontalAlignment="Center"
Margin="2"
Foreground="SteelBlue"
FontSize="18"/>
</Grid>
</Grid>
</Expander>
<!COMPANIES>
<Expander Grid.Row="2"
Header="Companies"
Margin="0,0,0,2"
ScrollViewer.VerticalScrollBarVisibility="Visible"
IsExpanded="{Binding AreCompaniesExpanded}">
<Grid VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ComboBox ItemsSource="{Binding CompanyFilters}">
<ComboBox.ItemTemplate>
<DataTemplate>
<mctrls:MaroisRadioButton IsChecked="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Content="{Binding Caption}"
GroupName="companyFilters"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Grid.Column="1"
Height="22"
Width="22"
Margin="2"
ToolTip="Reresh Companies"
Command="{Binding RefreshCompaniesCommand}">
<Image Source="/Jayhawk.UI.WPF;component/Media/Images/refresh.png"/>
</Button>
</Grid>
<mctrls:MaroisTreeView Grid.Row="1"
ItemsSource="{Binding Companies}"
SelectedItemEx="{Binding SelectedCompany}"
BorderThickness="0"
BorderBrush="Transparent"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Margin="1"/>
<StackPanel Orientation="Vertical"
Grid.Row="1"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Visibility="{Binding CompanyProgressVisible}">
<progress:SpinningProgress Height="54"
Width="65"
Margin="5"/>
<TextBlock Text="Loading..."
HorizontalAlignment="Center"
Margin="2"
Foreground="SteelBlue"
FontSize="18"/>
</StackPanel>
</Grid>
</Expander>
</Grid>
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Most of the time, you want "Auto", not "*".
When you use "*", it is typically for the most dynamic / expandable row and / or column; or it is used to take up extra slack "in the container".
When you use more than one "*"; you are asking for rows / columns of proportionally the same size.
If the container is 100, and your "Auto" is 10, then if you have two "*"; you will get "2 of 45".
Also, starting a window "maximized", can affect the proper working of scrollbars; starting it resizable first can help.
And "center" alignment versus stretch will also play a part; while stack panels typically don't "stretch".
And grids-within-grids requires careful planning because one "wrong" Auto or "*", will throw everything "off".
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
modified 2-Mar-18 16:13pm.
|
|
|
|
|
Hello.
I am writing code Highlight Searched Text with ListView in WPF.
The problem is that when I search a specific word, only match words in the screen visible are hightlighted but the other words in
listviewItems that should be highlighted which are located in the bottom of the listview are not highlighted.
After I run the program and I scroll down manually to the bottom in the list view and when I search the word, then it works well.
What I have tried:
To solve the problem, I added the code like below. Listview scrolls down and up properly like I intented to but the listItems does not highlight still.
// scroll to bottom for the list to make sure every item is in the view
if (listview.Items.Count > 1) listview.ScrollIntoView(listview.Items[listview.Items.Count - 1]);
// scroll back to top to make sure everything looks correct
if (listview.Items.Count > 1) listview.ScrollIntoView(listview.Items[0]);
|
|
|
|
|
|
You're making assumptions based on things you cannot "see".
It's not even confirmed that you are actually "selecting words" at the "bottom"; since you cannot "see" them.
And I've never heard "selected words" used in the context of a listview.
(Is "MultiSelect" on?)
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|
We have some code which receives images of a document from a C++ DLL, and displays them in a WPF application. The C# code stores the images in an array, which it flushes each time a new document is placed on the scanner. The code converts the image from a Bitmap to a BitmapImage, which is the image type for display in WPF:
private BitmapImage CreateBitmapImage(Bitmap bitmap)
{
using (MemoryStream memory = new MemoryStream())
{
bitmap.Save(memory, ImageFormat.Bmp);
memory.Position = 0;
BitmapImage bitmapimage = new BitmapImage();
bitmapimage.BeginInit();
bitmapimage.StreamSource = memory;
bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
bitmapimage.EndInit();
bitmapimage.Freeze();
return bitmapimage;
}
}
The above code leaks, but only on some PCs. On most it is okay. It can be fine on one Windows 10 PC, and leak on another that has Windows 10 on the same hardware.
A key point to note is that the DLL module is also passing the bitmap handle to a third party validation DLL, and if we remove that DLL, the memory leak disappears.
If I remove the Freeze() call, the code does not leak, but the images are not displayed.
If I comment out the return call, and return null instead, the code still leaks. Therefore the issue is not that we do not clear the stored images.
That this only occurs on some PCs, might suggest a timing issue associated with the validation DLL.
Thoughts?
Note: Edited to correct the code. For some reason I'd copied it with a key line missing!
modified 22-Feb-18 11:29am.
|
|
|
|