Reputation: 23
I'm trying to make a proper layout for a set of Image objects in WPF constructor.
What are my problems now:
1) I can't properly align Image elements. As you can see from the image, stuff is misplaced all over. I think I can place everything where I need to after some time, but here comes problem number 2.
2) If I will try to resize the window (the application will work fullscreen, but I need it to work on any resolution) the elements won't resize. I failed to find if they can be resized without making code specially for it, since Height and Width are defined in XAML.
Let me explain what I'm trying to achieve in case I'm using entirely wrong approach.
In the program I should display a person and his organs. The images may vary, but images of the same type have same width and height. For displaying I'm using a set of Image objects. ImageSource property is set programmatically depending on what I need to display. Here is the image to understand clearer.
https://i.sstatic.net/l2VX9.png
Here is what I'm using in a picture above.
<!-- View Patient -->
<TabItem x:Name="tabPatientView" Header="Patient View" >
<Grid Background="#FFE5E5E5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="btnViewPatientBack" Content="Назад" Grid.Column="0" Click="BtnNavMenuPatients_Click"/>
<Image x:Name="imgPatientViewHead" Grid.Column="1" HorizontalAlignment="Left" Margin="682,45,0,0" VerticalAlignment="Top" Width="100" Source="Resources/Bodies/Human/Male/Head.png"/>
<Image x:Name="imgPatientViewBody" Grid.Column="1" HorizontalAlignment="Left" Height="234" Margin="639,132,0,0" VerticalAlignment="Top" Width="158" Source="Resources/Bodies/Human/Male/Body.png"/>
<Image x:Name="imgPatientViewArmRight" Grid.Column="1" HorizontalAlignment="Left" Height="86" Margin="402,105,0,0" VerticalAlignment="Top" Width="252" Source="Resources/Bodies/Human/Male/RightArm.png"/>
<Image x:Name="imgPatientViewArmLeft" Grid.Column="1" HorizontalAlignment="Left" Height="86" Margin="812,105,0,0" VerticalAlignment="Top" Width="252" Source="Resources/Bodies/Human/Male/LeftArm.png"/>
<Image x:Name="imgPatientViewLegRight" Grid.Column="1" HorizontalAlignment="Left" Height="216" Margin="645,339,0,0" VerticalAlignment="Top" Width="85" Source="Resources/Bodies/Human/Male/RightLeg.png"/>
<Image x:Name="imgPatientViewLegLeft" Grid.Column="1" HorizontalAlignment="Left" Height="216" Margin="730,339,0,0" VerticalAlignment="Top" Width="92" Source="Resources/Bodies/Human/Male/LeftLeg.png"/>
<Image x:Name="imgPatientViewBrain" Grid.Column="1" HorizontalAlignment="Left" Height="40" Margin="694,45,0,0" VerticalAlignment="Top" Width="78" Source="Resources/Bodies/Human/Male/Brain.png"/>
<Image x:Name="imgPatientViewHeart" Grid.Column="1" HorizontalAlignment="Left" Height="59" Margin="740,132,0,0" VerticalAlignment="Top" Width="67" Source="Resources/Bodies/Human/Male/Heart.png"/>
<Image x:Name="imgPatientViewBreath" Grid.Column="1" HorizontalAlignment="Left" Height="99" Margin="671,120,0,0" VerticalAlignment="Top" Width="126" Source="Resources/Bodies/Human/Male/Breath.png"/>
<Image x:Name="imgPatientViewLiver" Grid.Column="1" HorizontalAlignment="Left" Height="39" Margin="659,224,0,0" VerticalAlignment="Top" Width="71" Source="Resources/Bodies/Human/Male/Liver.png"/>
<Image x:Name="imgPatientViewKidneys" HorizontalAlignment="Left" Height="55" VerticalAlignment="Top" Width="148" Margin="659,279,0,0" Grid.Column="1" Source="Resources/Bodies/Human/Male/Kidneys.png"/>
<Image x:Name="imgPatientViewStomach" Grid.Column="1" HorizontalAlignment="Left" Height="41" Margin="701,233,0,0" VerticalAlignment="Top" Width="71" Source="Resources/Bodies/Human/Male/Stomach.png"/>
<Image x:Name="imgPatientViewIntestines" Grid.Column="1" HorizontalAlignment="Left" Height="57" Margin="682,263,0,0" VerticalAlignment="Top" Width="68" Source="Resources/Bodies/Human/Male/Intestines.png"/>
<Image x:Name="imgPatientViewBladder" Grid.Column="1" HorizontalAlignment="Left" Height="33" Margin="716,292,0,0" VerticalAlignment="Top" Width="37" Source="Resources/Bodies/Human/Male/Bladder.png"/>
<Image x:Name="imgPatientViewReproduction" HorizontalAlignment="Left" Height="33" VerticalAlignment="Top" Width="37" Margin="716,325,0,0" Grid.Column="1" Source="Resources/Bodies/Human/Male/Reproduction.png"/>
<Image x:Name="imgPatientViewSpleen" Grid.Column="1" HorizontalAlignment="Left" Height="26" Margin="781,205,0,0" VerticalAlignment="Top" Width="25" Source="Resources/Bodies/Human/Male/Spleen.png"/>
</Grid>
</TabItem>
Is there any way to make that thing easier? I really think that I'm doing many unnecessary work there. Maybe there is some control element for such case. Can't say that I'm familar with WPF yet.
Upvotes: 2
Views: 327
Reputation: 414
Alternatively, If you didn't want to take the ViewModel + ListView approach I mentioned in another answer. You could just do this:
<TabItem x:Name="tabPatientView" Header="Patient View">
<ViewBox>
<Canvas Height="100" Width="100">
<Image x:Name="imgPatientViewHead" Canvas.Top="Y" Canvas.Right="X" Height="26" Stretch="Uniform" Source="Resources/Bodies/Human/Male/Head.png"/>
...
<Image x:Name="imgPatientViewSpleen" Canvas.Top="Y" Canvas.Right="X" Height="26" Stretch="Uniform" Source="Resources/Bodies/Human/Male/Spleen.png"/>
</Canvas>
</ViewBox>
</TabItem>
Note: If you explicitly set the height of the canvas then you will make each image height relative to that set height. And obviously the values for Canvas.Top and Canvas.Right need to be numbers not X and Y...
Upvotes: 1
Reputation: 21
Wraping all the Image elements within a Canvas element and then within a ViewBox element might help you.
The viewbox contained in a Grid element will resize when the parent window resizes and the inner image will resize to the viewbox scale.
For example:
<Viewbox x:Name="mViewBox" Stretch="Uniform" >
<Canvas Width="500" Height="500">
<Image x:Name="imgPatientViewHead" Grid.Column="1" HorizontalAlignment="Left" Margin="682,45,0,0" VerticalAlignment="Top" Width="100" Source="Resources/Bodies/Human/Male/Head.png"/>
<Image x:Name="imgPatientViewBody" Grid.Column="1" HorizontalAlignment="Left" Height="234" Margin="639,132,0,0" VerticalAlignment="Top" Width="158" Source="Resources/Bodies/Human/Male/Body.png"/>
<Image x:Name="imgPatientViewArmRight" Grid.Column="1" HorizontalAlignment="Left" Height="86" Margin="402,105,0,0" VerticalAlignment="Top" Width="252" Source="Resources/Bodies/Human/Male/RightArm.png"/>
<Image x:Name="imgPatientViewArmLeft" Grid.Column="1" HorizontalAlignment="Left" Height="86" Margin="812,105,0,0" VerticalAlignment="Top" Width="252" Source="Resources/Bodies/Human/Male/LeftArm.png"/>
<Image x:Name="imgPatientViewLegRight" Grid.Column="1" HorizontalAlignment="Left" Height="216" Margin="645,339,0,0" VerticalAlignment="Top" Width="85" Source="Resources/Bodies/Human/Male/RightLeg.png"/>
<Image x:Name="imgPatientViewLegLeft" Grid.Column="1" HorizontalAlignment="Left" Height="216" Margin="730,339,0,0" VerticalAlignment="Top" Width="92" Source="Resources/Bodies/Human/Male/LeftLeg.png"/>
<Image x:Name="imgPatientViewBrain" Grid.Column="1" HorizontalAlignment="Left" Height="40" Margin="694,45,0,0" VerticalAlignment="Top" Width="78" Source="Resources/Bodies/Human/Male/Brain.png"/>
<Image x:Name="imgPatientViewHeart" Grid.Column="1" HorizontalAlignment="Left" Height="59" Margin="740,132,0,0" VerticalAlignment="Top" Width="67" Source="Resources/Bodies/Human/Male/Heart.png"/>
<Image x:Name="imgPatientViewBreath" Grid.Column="1" HorizontalAlignment="Left" Height="99" Margin="671,120,0,0" VerticalAlignment="Top" Width="126" Source="Resources/Bodies/Human/Male/Breath.png"/>
<Image x:Name="imgPatientViewLiver" Grid.Column="1" HorizontalAlignment="Left" Height="39" Margin="659,224,0,0" VerticalAlignment="Top" Width="71" Source="Resources/Bodies/Human/Male/Liver.png"/>
<Image x:Name="imgPatientViewKidneys" HorizontalAlignment="Left" Height="55" VerticalAlignment="Top" Width="148" Margin="659,279,0,0" Grid.Column="1" Source="Resources/Bodies/Human/Male/Kidneys.png"/>
<Image x:Name="imgPatientViewStomach" Grid.Column="1" HorizontalAlignment="Left" Height="41" Margin="701,233,0,0" VerticalAlignment="Top" Width="71" Source="Resources/Bodies/Human/Male/Stomach.png"/>
<Image x:Name="imgPatientViewIntestines" Grid.Column="1" HorizontalAlignment="Left" Height="57" Margin="682,263,0,0" VerticalAlignment="Top" Width="68" Source="Resources/Bodies/Human/Male/Intestines.png"/>
<Image x:Name="imgPatientViewBladder" Grid.Column="1" HorizontalAlignment="Left" Height="33" Margin="716,292,0,0" VerticalAlignment="Top" Width="37" Source="Resources/Bodies/Human/Male/Bladder.png"/>
<Image x:Name="imgPatientViewReproduction" HorizontalAlignment="Left" Height="33" VerticalAlignment="Top" Width="37" Margin="716,325,0,0" Grid.Column="1" Source="Resources/Bodies/Human/Male/Reproduction.png"/>
<Image x:Name="imgPatientViewSpleen" Grid.Column="1" HorizontalAlignment="Left" Height="26" Margin="781,205,0,0" VerticalAlignment="Top" Width="25" Source="Resources/Bodies/Human/Male/Spleen.png"/>
</Canvas>
</Viewbox>
Upvotes: 1
Reputation: 3822
You should use the Canvas control. It allows you to enter specific coordinates for each image.
If you need to resize images, you can set an explicit width or height and it will resize for you (see https://learn.microsoft.com/en-us/dotnet/framework/wpf/controls/how-to-use-the-image-element).
You also have the option of using render transforms https://learn.microsoft.com/en-us/dotnet/framework/wpf/graphics-multimedia/transforms-overview
Then if you need the entire Canvas to scale (for a resized window or parent control for example) then you can put the whole thing in a ViewBox control.
One problem with images is they will get blurry as they scale up in a ViewBox (or in anything really). If you have the original images in a Vector format, you could convert them to Xaml using Inkscape or a similar program. That will prevent the pixelation and may even bring the images sizes down. It may not matter in your case though.
Upvotes: 1
Reputation: 414
I would definitely use a ListView. Each of the images would be in a ListView item. The Listview ItemsPanel (the container the ListView uses to store its elements) would be a canvas. Then each of the listview items would contain an attached property for the canvas describing its placement.
Each image would be an instance of a class (ViewModel) implementing properties that you can bind to. Those properties would be how you describe the image location relative to each other or the canvas.
As far as your other problems relating to relative sizing and resolutions go. The solutions are really simple!
-Once you have your ListView done, or have this wrapped up in a UserControl, or whatever, Just wrap it in a ViewBox. XAML will take care of resizing all children elements for you.
-In terms or relative sizing and placement, make sure that your XAML Image element has Stretch set to "Uniform" (so that the aspect ratio for each image is always preserved). Then in code, for each body part you only need to set one of the dimensions (width or height) to a relative size compared to your canvas, assuming the body picture is the largest image and is the one dictating the canvas size.
Here's an example of how you could create your ListView:
<ListView
Name="BodyListView"
ItemsSource="{Binding BodyParts}"
SelectionMode="Single"
ItemContainerStyle="{DynamicResource SelectBodyPartListViewItem}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid >
<Image Source="{Binding Image}" Canvas.Top="{Binding CanvasTop}"
Canvas.Right="{Binding CanvasRight}" Height="{Binding ImgHeight}" Stretch="Uniform"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Upvotes: 1