【发布时间】:2021-12-31 15:16:34
【问题描述】:
在 ViewModel 中这个事件处理程序:
private void Model_MineAdded(object sender, MineEventArgs e)
{
if (_model.Mines.Count > Mines.Count)
{
Mines.Add(new Shape
{
X = _model.Mines[e.MineID].X,
Y = _model.Mines[e.MineID].Y,
Width = _model.Mines[e.MineID].Width,
Height = _model.Mines[e.MineID].Height,
Weight = _model.Mines[e.MineID].Weight
}); // this line throws the exception
}
}
抛出此异常:
System.InvalidCastException '指定的强制转换无效。'
更多详情
游戏应用程序有一个(其他)问题,我必须使用 Xamarin.Forms(和 MVVM)编写。 我已经问过一个关于这个游戏的问题(1:Binding property in Xamarin.Forms using MVVM)。 该应用程序是一个简单的 2D 游戏,用户控制潜艇并且有地雷掉落,因此用户必须避开这些地雷。 潜艇可以以四种方式移动(上、下、左、右)。
我已经使用 WPF(和 WinForms)制作了这个游戏。使用 WPF 更容易,我使用 Canvas 用于游戏区域,ItemsControl 用于地雷(因为这些是在运行时使用计时器生成的),但在 Xamarin 中我真的不知道发生了什么在。我使用RelativeLayout 表示游戏区域,首先使用CollectionView 表示地雷,但地雷并没有做任何事情(至少它们被显示了)。现在我为地雷使用ListView,但收到了这个异常:
System.InvalidCastException '指定的强制转换无效。'
所以问题是如何显示游戏区域和地雷,以便游戏在使用 MVVM 架构的 Xamarin.Forms 中正常运行?
这些是相关的代码sn-ps:
GamePage.xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SubmarineGame.View.GamePage"
Title="Submarine Game">
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<RelativeLayout Grid.Row="0"
RelativeLayout.WidthConstraint="{Binding GameAreaWidth}"
RelativeLayout.HeightConstraint="{Binding GameAreaHeight}">
<Image RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=1}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Height,
Factor=1}"
Aspect="AspectFill"
Source="sea.png" />
<Image RelativeLayout.WidthConstraint="{ConstraintExpression Constant=50}"
RelativeLayout.HeightConstraint="{ConstraintExpression Constant=50}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=0.9}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Height,
Factor=0.1}"
Source="pausebutton.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ExitCommand}" />
</Image.GestureRecognizers>
</Image>
<Image RelativeLayout.WidthConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.HeightConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=0.8}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=0.9}"
Source="downarrow.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding StepCommand}" CommandParameter="Down" />
</Image.GestureRecognizers>
</Image>
<Image RelativeLayout.WidthConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.HeightConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=0.8}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=0.7}"
Source="uparrow.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding StepCommand}" CommandParameter="Up" />
</Image.GestureRecognizers>
</Image>
<Image RelativeLayout.WidthConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.HeightConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=0.7}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=0.8}"
Source="leftarrow.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding StepCommand}" CommandParameter="Left" />
</Image.GestureRecognizers>
</Image>
<Image RelativeLayout.WidthConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.HeightConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.XConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=0.9}"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=0.8}"
Source="rightarrow.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding StepCommand}" CommandParameter="Right" />
</Image.GestureRecognizers>
</Image>
<!--<CollectionView ItemsSource="{Binding Mines}"
ItemsLayout="HorizontalList"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
BackgroundColor="Transparent"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=1}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Height,
Factor=1}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Image RelativeLayout.WidthConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.HeightConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.XConstraint="{Binding XC}"
RelativeLayout.YConstraint="{Binding YC}"
Source="nuclearbomb.png" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>-->
<ListView ItemsSource="{Binding Mines}"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
BackgroundColor="Transparent">
<ListView.ItemTemplate>
<DataTemplate>
<Image RelativeLayout.WidthConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.HeightConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.XConstraint="{Binding XC}"
RelativeLayout.YConstraint="{Binding YC}"
Source="nuclearbomb.png"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Image RelativeLayout.WidthConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.HeightConstraint="{ConstraintExpression Constant=64}"
RelativeLayout.XConstraint="{Binding Submarine.XC}"
RelativeLayout.YConstraint="{Binding Submarine.YC}"
Source="submarine.png" />
</RelativeLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="Start" Grid.Row="1">
<Label Text="Game time: " />
<Label Text="{Binding GameTime}" />
</StackLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="End" Grid.Row="1">
<Label Text="Destroyed mines: " />
<Label Text="{Binding DestroyedMineCount}" />
</StackLayout>
</Grid>
</ContentPage.Content>
</ContentPage>
ViewModel 中的 Shape 类(由于转换错误 (RelativeLayout),需要 XC 和 YC 属性):
public class Shape : ViewModelBase
{
private Int32 _x;
private Int32 _y;
public Int32 X
{
get { return _x; }
set
{
if (_x != value)
{
_x = value;
OnPropertyChanged();
OnPropertyChanged(nameof(XC));
}
}
}
public Int32 Y
{
get { return _y; }
set
{
if (_y != value)
{
_y = value;
OnPropertyChanged();
OnPropertyChanged(nameof(YC));
}
}
}
public Int32 Width { get; set; }
public Int32 Height { get; set; }
public Int32 Weight { get; set; }
public Constraint XC { get { return Constraint.Constant(X); } }
public Constraint YC { get { return Constraint.Constant(Y); } }
}
ViewModel 中的Mines 和Submarine:
public ObservableCollection<Shape> Mines { get; set; }
public Submarine Submarine { get; set; }
模型中的Shape 类(持久性):
public enum ShapeType { Submarine, Mine }
public class Shape
{
public ShapeType Type { get; set; }
public Int32 X { get; set; }
public Int32 Y { get; set; }
public Int32 Width { get; private set; }
public Int32 Height { get; private set; }
public Int32 Weight { get; set; }
public Shape(ShapeType type, Int32 startX, Int32 startY, Int32 width, Int32 height, Int32 weight)
{
Type = type;
X = startX;
Y = startY;
Width = width;
Height = height;
Weight = weight;
}
}
我认为这些是项目中最相关的部分,我希望我不会错过任何东西。 感谢您的宝贵时间,对不起我的英语!
【问题讨论】:
-
你调查过 SkiaSharp 吗?它提供了一个绘图画布。我不确定 ListView 或 CollectionView 是否是游戏的好选择。如果您包含您希望 UI 外观的屏幕截图或模型,这将非常有帮助。
-
也就是说,您实际提出的“如何显示游戏区域和地雷以使游戏正常运行”的问题过于广泛而无法回答,尤其是我们不知道是什么“游戏区”是,或者“游戏正常运行”是什么意思。
-
最后,你得到的异常 - 我们不知道是什么行导致它,所以不可能确定问题是什么。您需要查看堆栈跟踪或在调试器中捕获异常以确定哪一行是源。知道这一点将使我们能够帮助您修复它。请在发帖前阅读How to Ask 以获得有关编写主题问题的指导。
-
感谢@Jason 的发言!我编辑了这个问题,所以我希望它现在会好起来!是的,我尝试过 SkiaSharp,但我认为深度不够,因此也感谢您的建议!
-
我认为只使用 AbsoluteLayout 可能是一个更好的主意。但是您仍然没有解释
InvalidCastException的来源。而且您的问题仍然非常广泛。
标签: c# xaml xamarin.forms mvvm