Asdolg
Asdolg

Reputation: 23

How to set proper layout for a set of Images in C# WPF?

Which problems I had encountered

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.

What I am trying to do

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

How XAML looks like

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

Answers (4)

Smolakian
Smolakian

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

Efrain Hernandez
Efrain Hernandez

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

Ben Zuill-Smith
Ben Zuill-Smith

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

Smolakian
Smolakian

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

Related Questions