Sometimes the images in ListView disappear - wpf

A list of countries with flags is displayed in ListView. Flags are displayed as an ellipse with Fill ImageBrush.
<ControlTemplate TargetType="ContentControl">
<StackPanel Orientation="Horizontal" Background="Transparent">
<Ellipse Width="23" Height="23" StrokeThickness="1" Stroke="#2F7AE5" Fill="{Binding FlagBrush}"/>
<TextBlock Text="{Binding Path=Country}"
Foreground="{StaticResource HBBtnTextBrush}" FontSize="16" FontWeight="Bold"
VerticalAlignment="Center" Margin="14,0,0,0"/>
<ContentPresenter/>
</StackPanel>
</ControlTemplate>
public ImageSource FlagImage { get => Flag.GetImage(country_code); }
ImageBrush flagBrush;
public ImageBrush FlagBrush {
get
{
if (flagBrush == null)
{
flagBrush = new ImageBrush
{
ImageSource = FlagImage,
Stretch = Stretch.UniformToFill
};
flagBrush.Freeze();
}
return flagBrush;
}
}
One or two images in the hidden part of the list disappear.
Images are stored in resources as DrawingImage with GeometryDrawing.
If you go to another page and then return these images will appear, but others may disappear.
In the debugger, these images are inherent in ImageSource, and if you change any image parameter, for example, position or resize, the image immediately appears.

Solved the problem with a dirty hack. I added WrapPanel to the screen in an empty area and displayed there all possible flags of 1x1 pixel and Opacity=0.01. After that, there was no problem with disappearing flags.

Related

Helix3d WPF draw text at the top/left of HelixViewport3D

I want to show some text information at the top/left of HelixViewport3D like "ShowCameraInfo" does, which display camera information at the bottom/left of the Viewport. BillboardTextVisual3D requires a 3D point, but what I want is just like TextBlock on Canvas, which just need a 2D coordinate.
I can use TextBlock, but it cannot be captured as part of HelixViewport3D.
Any idea?
Literally a Textblock placed over the HelixViewport3D should be what you need.
There is only one problem: Viewport will not recognize if you try to manipulate the camera by initiating the mouse click on the Stackpanel.
<Grid>
<hx:HelixViewport3D>
<hx:DefaultLights/>
<hx:CubeVisual3D SideLength="7"/>
<hx:CubeVisual3D SideLength="5" Fill="Red" Center="-5,3,0"/>
</hx:HelixViewport3D>
<StackPanel Margin="5" HorizontalAlignment="Left" VerticalAlignment="Top">
<StackPanel.Background>
<SolidColorBrush Color="#FFB6B6B6" Opacity="0.4"/>
</StackPanel.Background>
<TextBlock Text="asdhfasdvfmnas" Margin="5,2"/>
<TextBlock Text="mvcbnxcvjhbkdaf" Margin="5,2"/>
<TextBlock Text="vbkjsdvj" Margin="5,2"/>
</StackPanel>
</Grid>

WPF Grid set background color and png image

I have Grid (3x3) in my main window and want to have a background color (which changes when I press button in other window) and background image (which also changes on click of same button) in the first Grid cell (Row="0", Column="0").
MainWindow XAML (code just for first Grid (0,0)):
<Grid x:Name="Q1Grid"
Grid.Row="0"
Grid.Column="0">
<Border x:Name="Q1Border"
Grid.Row="0"
Grid.Column="0"
>
<TextBlock x:Name="Q1TB"
Style="{StaticResource CategoryTextBlock}"
Text="Category 1"
MouseLeftButtonUp="Q1_MouseLeftButtonUp"/>
<Border.Background>
<ImageBrush x:Name="Q1P"
ImageSource="{DynamicResource Transparent}"
Stretch="Uniform"/>
</Border.Background>
</Border>
</Grid>
In another window (Q1) I have this code:
private void Q1C_Click(object sender, RoutedEventArgs e)
{
((MainWindow)System.Windows.Application.Current.MainWindow).Q1TB.Visibility = Visibility.Hidden;
((MainWindow)System.Windows.Application.Current.MainWindow).Q1Grid.Background = Brushes.AliceBlue;
var correct = new BitmapImage(new Uri("pack://application:,,,/Pictures/Joc pravilen - transparent.png"));
((MainWindow)System.Windows.Application.Current.MainWindow).Q1P.ImageSource = correct;
this.Close();
}
I can only get background color and not image or vice-versa, but I can not get both. I tried many options:
Set background color to Grid (0,0), but then due to round corners, background color is out ob those corners
Set color and image to Border and/or TextBlock and none)
None gave me the wanted result. Does anybody have a solution?
You cannot set the Background of an element twice, but you can add a second border Q1BackgroundBorder that displays the background color and is behind the Q1Border.
<Grid x:Name="Q1Grid"
Grid.Row="0"
Grid.Column="0">
<Border x:Name="Q1BackgroundBorder"/>
<Border x:Name="Q1Border"
Grid.Row="0"
Grid.Column="0">
<!-- ...other XAML code. -->
</Border>
</Grid>
Set the background of the Q1BackgroundBorder in your Q1C_Click click handler.
((MainWindow)System.Windows.Application.Current.MainWindow).Q1BackgroundBorder.Background = Brushes.AliceBlue;

Textbox will not stretch to fill viewbox

I want the font size of my labels and textboxes in my LOB form to grow and shrink with window resize or resolution change. To achieve this I've placed my labels and textboxes within viewboxes.
The labels and custom radio buttons behave as I expect, but the textboxes will not stretch horizontally to fill the viewbox (sorry can't post image because of rep). The textboxes will horizontally fill the viewbox if you type into them.
Here is an example of the code which I am working with:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.186*"/>
<ColumnDefinition Width="0.814*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.127*"/>
<RowDefinition Height="0.873*"/>
</Grid.RowDefinitions>
<Viewbox Margin="0,0,0.917,0">
<Label Content="First name:"/>
</Viewbox>
<Viewbox Grid.Column="1">
<TextBox TextWrapping="Wrap"/>
</Viewbox>
</Grid>
I've tried placing grid, stackpanel and dockpanel (with LastChildFill="True") within the viewbox and then placing the textboxes within these layout controls but this didn't work either. Is there anyway to get the textboxes to horizontally fill the parent viewbox?
This problem is similar to this: WPF TextBox won't fill in StackPanel but this problem is with stackpanels, not viewboxes.
I think what you want is not easily possible. ViewBox tells its children that they have inifite space, in the measure pass of the layouting. After that, the display is fit into the viewbox. Now a textbox, told to have infinite space, obviously can't stretch.
I have 2 solutions, which i think are not what you want, but might be helpful anyway.
The first:
<Viewbox Grid.Column="1" Grid.Row="0" Stretch="Uniform" >
<Grid Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Viewbox}}, Path=ActualWidth}">
<TextBox TextWrapping="Wrap" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
</Viewbox>
this will stretch you textbox infact, but disable the behaviour expected from the viewbox. Why? We told the ViewBox to keep the aspect ratio, and set the textbox to fill the whole width of the viewbox, which keeps the size of the textbox.
The second:
would be to add a height to the grid, taken from the label, modified with the scaletransform of its viewbox.
This one i haven't tried, but it would involve a value converter.
In conclusion: There is no easy way to achieve what you want, because of the way the viewbox layouts its children. If you just want to stretch horizontally, my first solution works fine, if you want to scale. My guess is you have to do it yourself.
If what you want doesn't work/isn't easy then fake it:
<TextBox GotFocus="TextBox_GotFocus" HorizontalAlignment="Stretch" BorderThickness="0" Grid.Column="1"/>
<Viewbox HorizontalAlignment="Left" Stretch="Uniform" Grid.Column="1">
<TextBox x:Name="NameTextBox" Width="50" TextWrapping="Wrap" Text="Text" BorderThickness="0"/>
</Viewbox>
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
NameTextBox.Focus();
NameTextBox.SelectionStart = NameTextBox.Text.Length;
}
Basically what happens is another TextBox is behind the Viewbox and when the behind TextBox gets focus, it switches focus to the Viewbox's TextBox. This will produce some odd resizing since you have your grid setup with relative sizes. You will need to play around with your grid column/width sizes until you get the effect you desire.
Just use a converter
Set the FontSize like this:
FontSize="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight ,Converter={StaticResource heightconverter}, ConverterParameter=3}"
public class ProcentualHeightConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter is string p)
{
if (value is double v)
{
var result = double.TryParse(p, out double param);
if (result)
{
return v / param;
}
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Cannot access drag adorner template

I've used the sample code provided by Bea Stollnitz (http://bea.stollnitz.com/blog/?p=53), in order to enable drag and drop in my application, and drag adorner, etc.
Everything works fine, my drag adorner is well displayed, I have all the behavior I want.
But (yes there is always a but), I cannot access the DataTemplate of the Drag Adorner, in order to display different data depending on the dragged data.
I have simplified the code, but the basics are still there.
This is the DataTemplate of my DragAdorner
<DataTemplate x:Key="DragAndDropTemplate" DataType="{x:Type MyType}">
<Grid>
<Grid Opacity="0.5">
<Border x:Name="HeaderBorder" CornerRadius="2" BorderThickness="1" Margin="5,2,5,2">
<Border x:Name="InsideBorder" CornerRadius="2" BorderThickness="1">
<TextBlock x:Name="number" Text="{Binding Name}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/>
</Border>
</Border>
</Grid>
<Border Width="17" Height="17" BorderBrush="White" HorizontalAlignment="Center" VerticalAlignment="Center" CornerRadius="1" x:Name="numberContainer" Visibility="Collapsed">
<TextBlock x:Name="number" Text="80" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/>
</Border>
</Grid>
</DataTemplate>
This is the code that create the Adorner :
if (this.draggedAdorner == null)
{
var adornerLayer = AdornerLayer.GetAdornerLayer(source);
this.draggedAdorner = new DraggedAdorner(draggedData, dataTemplate, source, adornerLayer);
}
And this is the code that init an adorner
public DraggedAdorner(List dragDropData, DataTemplate dragDropTemplate, FrameworkElement adornedElement, AdornerLayer adornerLayer)
: base(adornedElement)
{
this.adornerLayer = adornerLayer;
this.contentPresenter = new ContentPresenter();
this.contentPresenter.Content = dragDropData[0];
this.contentPresenter.ContentTemplate = dragDropTemplate;
this.adornerLayer.Add(this);
}
The draggedData, will be a list of MyType, I get the first item as the content of the ContentPresenter of my DraggedAdorner, so the DataTemplate can apply.
The problem is, I want to access the numberContainer and number control of the DataTemplate, in order to display the number of dragged object, in the adorner. But I cannot manage to access it, whatever I try, It ends with the "This operation is valid only on elements that have this template applied." message.
I have tought I could do something like this :
this.contentPresenter.ContentTemplate.FindName("number", this.contentPresenter);
Since the DataTemplate should apply to the ContentPresenter, but nope...
For information the adornedElement is the ListViewItem from which the drag occurs.
If you have any idea...
Ok, so I have found how to achieve what I wanted.
I don't know why it didn't comes to mind earlier, and why I didn't found anything about this before.
I have just added a single line before trying to access the template :
this.UpdateLayout()
Looks like it forces the ContentPresenter and DataTemplate object to be update and "re-rederend" so the ContentPresenter is really templated by my DataTemplate.

Binding a Viewbox to a Canvas

I'm trying to bind a Viewbox to a Canvas that is created dynamically like so:
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Viewbox>
<ContentPresenter Content="{Binding Canvas}"/>
</Viewbox>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
This works fine as long as the Canvas doesn't have any children, but as soon at the Canvas has children it's not shown. What am I missing here?
How do you know it works? A Canvas is just a Panel with zero width/height. Even if it has any children, its dimensions are still going to be 0,0. You must explicitly set Width and Height to a non-zero value in order for it to appear. Paste the following snippet into XamlPad or just test in your own app. Now, remove either Width or Height and it will vanish.
<Viewbox>
<ContentPresenter>
<ContentPresenter.Content>
<Canvas Background="Red" Width="1" Height="1">
<TextBlock Canvas.Left="10" Canvas.Top="20" Text="123" />
</Canvas>
</ContentPresenter.Content>
</ContentPresenter>
</Viewbox>
Forget I ever asked :-)
I caused an exception when creating the children of canvas, and this in turn caused the canvas to not be shown. I'm sad to say that it's not the first time I have made this mistake, and it's probably not that last time either:
TextBlock tb = new TextBlock();
tb.SetValue(Canvas.LeftProperty, 5);
tb.SetValue(Canvas.TopProperty, 5);
"5" is not a valid value for 'Left' or 'Top'. It should of course be
TextBlock tb = new TextBlock();
tb.SetValue(Canvas.LeftProperty, 5.0);
tb.SetValue(Canvas.TopProperty, 5.0);
And because it was created as part of data binding, no exception dialog was shown. All in all ... DOOOOH :-)
:-)

Resources