C
C#2mo ago
Rodonies

WPF Framework Canvas Automatic Scaling based on largest coordinate in list

Hi. First of all, thank you for reading! I have a MVVM WPF application and having some trouble with the Canvas and how to draw shapes that are automatically scaled to the canvas size. Let's say I have a model Y that contains a list of X. X has a location and my goal is to display all X's in a fixed size canvas using circles. The location of each X can vary wildly so for every list of X that gets selected I need to use the largest coordinate of the furthest X in that list to scale it so all the X's show up properly on the canvas, the furthest one should be located 10% away from the canvas' border. Assuming (0, 0) is the middle of my canvas the calculation for this should be
coordinate/(FurthestCoordinate/(0.9*CanvasSize/2))
coordinate/(FurthestCoordinate/(0.9*CanvasSize/2))
I am using the CalcBinding nuget so I can do calculations in my binding, like so:
{c:Binding (Location.X*6)+500}
{c:Binding (Location.X*6)+500}
The problem: I can't seem to use the LargestCoordinate property from the ancestor (which is Y) in my calculation for each X I can get that property in xaml correctly using:
{Binding Path=DataContext.LargestCoordinate, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Border}, Mode=OneWay}
{Binding Path=DataContext.LargestCoordinate, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Border}, Mode=OneWay}
but cannot use this method in my full calculation:
X="{c:Binding '(Location.X/({Binding Path=DataContext.LargestCoordinate, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Border}, Mode=OneWay}/(0.45*494))'}"
X="{c:Binding '(Location.X/({Binding Path=DataContext.LargestCoordinate, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Border}, Mode=OneWay}/(0.45*494))'}"
it just gives a whole lot of XAML binding errors like this one:
System.Windows.Data Warning: 40 : BindingExpression path error: 'FindAncestor' property not found on 'object' ''Planet' (HashCode=19050756)'. BindingExpression:Path=FindAncestor; DataItem='Planet' (HashCode=19050756); target element is 'TranslateTransform' (HashCode=48896832); target property is 'X' (type 'Double')
Is there any way I can use the LargestCoordinate property from Y in my calculation for each X so I can scale the locations properly?
5 Replies
Rodonies
Rodonies2mo ago
relevant code:
class Y {
public List<X> ListOfX;
public double LargestCoordinate { get { return XList.Max(o => o.Distance); } }
}

class X {
public Point Location { get; set; }
public double LargestCoordinate { get { return Math.Max(Math.Abs(Location.X), Math.Abs(Location.Y)); } }
}
class Y {
public List<X> ListOfX;
public double LargestCoordinate { get { return XList.Max(o => o.Distance); } }
}

class X {
public Point Location { get; set; }
public double LargestCoordinate { get { return Math.Max(Math.Abs(Location.X), Math.Abs(Location.Y)); } }
}
<ListBox x:Name="YListBox" Width="200" Height="333" ItemsSource="{Binding ListOfY}" SelectedItem="{Binding YListBoxSelectedItem}" />

<Border Width="494" Height="494" BorderThickness="2" BorderBrush="DarkGreen" DataContext="{Binding YListBoxSelectedItem}" Background="White">
<Canvas HorizontalAlignment="Center" VerticalAlignment="Center" Width="0" Height="0" RenderTransform="1 0 0 -1 0 0">
<Ellipse Name="Center" Width="10" Height="10" Fill="AliceBlue" Stroke="Black" StrokeThickness="2" />
<ItemsControl ItemsSource="{Binding ListOfX}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Width="8" Height="8" Fill="AliceBlue" Stroke="Black" StrokeThickness="2">
<Ellipse.RenderTransform>
<TranslateTransform X="{c:Binding --Calculation Here--}" Y="{c:Binding --Calculation Here--}" />
</Ellipse.RenderTransform>
</Ellipse>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
</Border>
<ListBox x:Name="YListBox" Width="200" Height="333" ItemsSource="{Binding ListOfY}" SelectedItem="{Binding YListBoxSelectedItem}" />

<Border Width="494" Height="494" BorderThickness="2" BorderBrush="DarkGreen" DataContext="{Binding YListBoxSelectedItem}" Background="White">
<Canvas HorizontalAlignment="Center" VerticalAlignment="Center" Width="0" Height="0" RenderTransform="1 0 0 -1 0 0">
<Ellipse Name="Center" Width="10" Height="10" Fill="AliceBlue" Stroke="Black" StrokeThickness="2" />
<ItemsControl ItemsSource="{Binding ListOfX}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Width="8" Height="8" Fill="AliceBlue" Stroke="Black" StrokeThickness="2">
<Ellipse.RenderTransform>
<TranslateTransform X="{c:Binding --Calculation Here--}" Y="{c:Binding --Calculation Here--}" />
</Ellipse.RenderTransform>
</Ellipse>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
</Border>
If there is a better way without Canvas or using a different method, feel free to chime in. I have also tried ViewBox but haven't gotten that one to work as well as the solution I posted above.
<Viewbox Width="494" Height="494" RenderTransform="1 0 0 -1 0 0">
<ItemsControl ItemsSource="{Binding ListOfX}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Width="8" Height="8" Fill="Red" Stroke="Black" StrokeThickness="2">
<Ellipse.RenderTransform>
<TranslateTransform X="{c:Binding --Calculation Here--}" Y="{c:Binding --Calculation Here--}" />
</Ellipse.RenderTransform>
</Ellipse>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Viewbox>
<Viewbox Width="494" Height="494" RenderTransform="1 0 0 -1 0 0">
<ItemsControl ItemsSource="{Binding ListOfX}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Width="8" Height="8" Fill="Red" Stroke="Black" StrokeThickness="2">
<Ellipse.RenderTransform>
<TranslateTransform X="{c:Binding --Calculation Here--}" Y="{c:Binding --Calculation Here--}" />
</Ellipse.RenderTransform>
</Ellipse>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Viewbox>
Rodonies
Rodonies2mo ago
Almost solved the issue myself, just need to figure out this next problem
<Path Name="Center" Fill="Blue" Stroke="Black" StrokeThickness="2">
<Path.Data>
<EllipseGeometry RadiusX="8" RadiusY="8">
<EllipseGeometry.Center>
<MultiBinding Converter="{StaticResource CConverter}">
<Binding Path="Width" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=Canvas}" Mode="OneWay" />
<Binding Path="Height" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=Canvas}" Mode="OneWay" />
<Binding Path="RadiusX" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=EllipseGeometry}" Mode="OneWay" />
<Binding Path="RadiusY" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=EllipseGeometry}" Mode="OneWay" />
</MultiBinding>
</EllipseGeometry.Center>
</EllipseGeometry>
</Path.Data>
</Path>
<Path Name="Center" Fill="Blue" Stroke="Black" StrokeThickness="2">
<Path.Data>
<EllipseGeometry RadiusX="8" RadiusY="8">
<EllipseGeometry.Center>
<MultiBinding Converter="{StaticResource CConverter}">
<Binding Path="Width" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=Canvas}" Mode="OneWay" />
<Binding Path="Height" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=Canvas}" Mode="OneWay" />
<Binding Path="RadiusX" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=EllipseGeometry}" Mode="OneWay" />
<Binding Path="RadiusY" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=EllipseGeometry}" Mode="OneWay" />
</MultiBinding>
</EllipseGeometry.Center>
</EllipseGeometry>
</Path.Data>
</Path>
I don't know why this following code can't get find the property RadiusX on the EllipseGeometry https://i.imgur.com/izux1tM.png
<Binding Path="RadiusX" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=EllipseGeometry}" Mode="OneWay" />
<Binding Path="RadiusX" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=EllipseGeometry}" Mode="OneWay" />
Imgur
Rodonies
Rodonies2mo ago
https://i.imgur.com/7Lr98Na.png it used to be able to find the old Ellipse element and get me the width and height, but it can't with EllipseGeometry?
Imgur
Rodonies
Rodonies2mo ago
I fully managed to get it working using this code
public class ObjectComparisonConverter : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value[0].Equals(value[1]);
}

public object[] ConvertBack(object value, Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class ObjectComparisonConverter : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value[0].Equals(value[1]);
}

public object[] ConvertBack(object value, Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class CoordinateConverterSingle : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double CanvasSize = (double)value[0];
if (value.Length == 1) return CanvasSize / 2;
double LargestCoordinate = (double)value[1];
double CoordinateX = (double)value[2];

//Scalefactor
double ScaleFactor = (0.45 * CanvasSize);

//Scale
double result = CoordinateX / (LargestCoordinate / ScaleFactor);

//Translate
result += 0.5 * CanvasSize;

return result;
}

public object[] ConvertBack(object value, Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class CoordinateConverterSingle : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double CanvasSize = (double)value[0];
if (value.Length == 1) return CanvasSize / 2;
double LargestCoordinate = (double)value[1];
double CoordinateX = (double)value[2];

//Scalefactor
double ScaleFactor = (0.45 * CanvasSize);

//Scale
double result = CoordinateX / (LargestCoordinate / ScaleFactor);

//Translate
result += 0.5 * CanvasSize;

return result;
}

public object[] ConvertBack(object value, Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Want results from more Discord servers?
Add your server