【问题标题】:System.InvalidCastException 'specified cast is not valid.'' System.InvalidCastException '指定的演员表无效。
【发布时间】: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 '指定的强制转换无效。'

这个 WPF 版本的屏幕截图可能有助于理解我要描述的内容:

所以问题是如何显示游戏区域和地雷,以便游戏在使用 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),需要 XCYC 属性):

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 中的MinesSubmarine

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


【解决方案1】:

以给出异常的语句的 8 行为例。找出确切的原因。

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

一次只做这一步 - 现在例外在哪里?

var shape = new Shape();
var mine = _model.Mines[e.MineID];
shape.X = mine.X;
shape.Y = ...;
...
Mines.Add(shape);

一个完整的测试版本,与你的比较。

请注意,我用RelativeLayout 代替ListView 来控制地雷(这里是Sprites)。在您的情况下,这将是您当前的 RelativeLayout 中的一个 RelativeLayout。我不推荐使用 ListView。

注意:如果您想让坐标随游戏区域自动缩放,请参阅XXConstraint

如您所见,目前需要为每个精灵的 X 和 Y 创建 XConstraintYConstraint 属性。

我把Sprites直接放在页面后面的代码上。如果您有单独的虚拟机,请将它们移到那里。

SpritesPage.xaml:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XFSOAnswers.SpritesPage">
    <ContentPage.Content>
        <Grid RowDefinitions="Auto, *" BackgroundColor="Pink" >
            <Label Grid.Row="0" Text="The Game" HorizontalOptions="Center" TextColor="Black" />
            <RelativeLayout x:Name="GameArea" Grid.Row="1" VerticalOptions="Fill"
                            BindableLayout.ItemsSource="{Binding Sprites}"
                            BackgroundColor="LightBlue" Margin="8,0,8,8">
                <BindableLayout.ItemTemplate>
                    <DataTemplate>
                        <!--RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor={Binding X}}"
                            RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor={Binding Y}}"-->
                        <Label Text="{Binding Name}"
                               RelativeLayout.XConstraint="{Binding XConstraint}"
                               RelativeLayout.YConstraint="{Binding YConstraint}"
                               />
                    </DataTemplate>
                </BindableLayout.ItemTemplate>
            </RelativeLayout>
        </Grid>
    </ContentPage.Content>
</ContentPage>

SpritesPage.xaml.cs:

using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace XFSOAnswers
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class SpritesPage : ContentPage
    {
        public SpritesPage()
        {
            InitializeComponent();
            BindingContext = this;
        }

        public ObservableCollection<Model3> Sprites { get; set; } = new ObservableCollection<Model3> {
            new Model3(),
            new Model3("b", 0.4, 0),
            new Model3("c", 0, 0.2),
        };

        protected override void OnAppearing()
        {
            base.OnAppearing();

            AnimationLoop();
        }

        private void AnimationLoop()
        {
            // Animation test.
            Device.BeginInvokeOnMainThread(async () =>
            {
                // HACK to make sure page is displayed before begin animation loop.
                await Task.Delay(500);

                for (int i = 0; i < 100; i++)
                {
                    await Task.Delay(100);
                    Sprites[0].X += 0.01;
                    Sprites[0].Y += 0.01;

                    // Might need this on some platforms.
                    //this.GameArea.ForceLayout();

                }
            });
        }
    }
}

Model3.cs:

using Xamarin.Forms;

namespace XFSOAnswers
{
    public class Model3 : BindableObject
    {
        public string Name { get; set; }

        // (x, Y) = (0.0, 0.0) at top-left, (1.0, 1.0) at bottom-right.
        // Currently, it doesn't seem to be possible to bind these directly.
        // RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor={Binding X}}"
        // gave error "No property, BindableProperty, or event found for "Factor", or mismatching type between value and property."
        public double X
        {
            get => _x;
            set
            {
                _x = value;
                OnPropertyChanged();
                OnPropertyChanged(nameof(XConstraint));
            }
        }
        private double _x;
        public Constraint XConstraint => Constraint.RelativeToParent((parent) => { return parent.Width * X; });

        public double Y
        {
            get => _y;
            set
            {
                _y = value;
                OnPropertyChanged();
                OnPropertyChanged(nameof(YConstraint));
            }
        }
        private double _y;
        public Constraint YConstraint => Constraint.RelativeToParent((parent) => { return parent.Height * Y; });


        public Model3(string name = "a", double x = 0, double y = 0)
        {
            Name = name;
            X = x;
            Y = y;
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-27
    • 2023-04-10
    • 2018-02-06
    • 1970-01-01
    相关资源
    最近更新 更多