3

我昨天发布了一个问题,但我认为我没有正确解释它。

让我再尝试一次。

所以这是我的目标:

在此处输入图像描述

红色气泡代表传入消息,蓝色气泡代表传出消息。我可以使用以下 xaml 代码更准确地描述这一点。请注意,以下代码只是对我的实际 xaml 代码(带有一些 DataTemplates)编译时我期望得到的结果的解释(WPF 将使用 DataTemplates 自动为我填充数据)。:

<ListBox>
    <ListBoxItem HorizontalAlignment="Right">
        <Grid Background="Blue">
            <TextBlock Text="Help me please!" FontSize="30"/>
        </Grid>
    </ListBoxItem>
    <ListBoxItem HorizontalAlignment="Left">
        <Grid Background="Red">
            <TextBlock Text="What do you want?" FontSize="30"/>
        </Grid>
    </ListBoxItem>
    <ListBoxItem HorizontalAlignment="Right">
        <Grid Background="Blue">
            <TextBlock Text="I want a ListBox" FontSize="30"/>
        </Grid>
    </ListBoxItem>
    <ListBoxItem HorizontalAlignment="Left">
        <Grid Background="Red">
            <TextBlock Text="Then?" FontSize="30"/>
        </Grid>
    </ListBoxItem>
    <ListBoxItem HorizontalAlignment="Right">
        <Grid Background="Blue">
            <TextBlock Text="But the Grid won't fill" FontSize="30"/>
        </Grid>
    </ListBoxItem>
</ListBox>

为了实现这一点,我写道:

<ListBox>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <ListBoxItem>
                <Grid Background="{Binding Color}">
                    <TextBlock Text="{Binding Text}" FontSize="30"/>
                </Grid>
            </ListBoxItem>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

请注意,上面的代码中没有指定对齐方式,因为我真的不知道如何使用模板分别为 ListBoxItem 设置不同的对齐方式。所以这会导致默认情况下所有蓝色和红色网格都向左对齐。

我的第一种方法包括一个数据模板选择器(传入消息的模板被省略):

<ListBox>
    <ListBox>
        <ListBox.ItemTemplate>
            <!-- local:MessageBubbleTemplateSelector.OutgoingMessageTemplate  -->
            <DataTemplate>
                <ListBoxItem>
                    <Grid>
                        <Grid Background="{Binding Color}" HorizontalAlignment="Right">
                            <TextBlock Text="{Binding Text}" FontSize="30"/>
                        </Grid>
                    </Grid>
                </ListBoxItem>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</ListBox>

但这没有用。因为Grid包裹语音气泡的那个不会自动膨胀,所以Grid里面的对齐Grid没关系(紧密贴合)。

然后我去搜索如何在 aGrid内部扩展 a StackPanel,但没有运气。

经过数小时的谷歌搜索和反复试验,我决定为ItemsPanelTemplate自己定义模板。我的Message对象中有一个属性,可以帮助我区分传入消息和传出消息。但我不知道如何创建ItemsPanelTemplate选择器(作为记录,谷歌告诉我Style.TriggerWindows Phone 8 不支持)。

所以我的问题是:如何设置不同的 Horizo​​ntalAlignment ListBoxItems

顺便说一句,ItemsPabelTemplate看起来像这样:

<ListBox.ItemsPanel>
    <ItemsPanelTemplate>
        <StackPanel />
    </ItemsPanelTemplate>
</ListBox.ItemsPanel>

非常感谢您的耐心等待。我已经在这里疯狂绝望了......浪费了这么多时间......

4

5 回答 5

5

注意:我没有 Phone SDK,所以只能使用普通的 WPF 应用程序。我没有使用你提到的触发器,它们不起作用。

所以我敲了一个看起来像这样的简单应用程序

在此处输入图像描述

这是代码:

应用程序.xaml.cs

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        var mainvm = new MainWindowViewModel();
        var window = new MainWindow
        {
            DataContext = mainvm
        };
        window.Show();

        mainvm.Messages.Add(new OutgoingMessage{ MessageContent = "Help me please!"});

        mainvm.Messages.Add(new IncomingMessage { MessageContent = "What do you want" });

        mainvm.Messages.Add(new OutgoingMessage { MessageContent = "I want a ListBox" });

        mainvm.Messages.Add(new IncomingMessage { MessageContent = "Then?" });

        mainvm.Messages.Add(new OutgoingMessage { MessageContent = "But the Grid won't fill" });
    }
}

主窗口.xaml

<Window x:Class="ChatUI.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ChatUI"
    Title="MainWindow" Height="350" Width="200">
<Window.Resources>
    <DataTemplate DataType="{x:Type local:IncomingMessage}">
        <Grid Margin="0,10">
            <Border CornerRadius="8" Background="Red" BorderBrush="Black" BorderThickness="1" />
            <TextBlock Text="{Binding MessageContent}" HorizontalAlignment="Left" Margin="5" Foreground="White"/>
        </Grid>
    </DataTemplate>

    <DataTemplate DataType="{x:Type local:OutgoingMessage}">
        <Grid Margin="0,10">
            <Border CornerRadius="8" Background="Blue" BorderBrush="Black" BorderThickness="1" />
            <TextBlock Text="{Binding MessageContent}" HorizontalAlignment="Right" Margin="5" Foreground="White"/>
        </Grid>
    </DataTemplate>
</Window.Resources>
<Grid Background="Black">
    <ItemsControl ItemsSource="{Binding Path=Messages}"/>
</Grid>

ViewModelBase.cs

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}

主窗口视图模型:

public class MainWindowViewModel : ViewModelBase
{
    public MainWindowViewModel()
    {
        Messages = new ObservableCollection<Message>();
    }
    public ObservableCollection<Message> Messages { get; protected set; }
}

消息.cs:

public abstract class Message : ViewModelBase
{
    private string _messageContent;

    public string MessageContent 
    {
        get
        {
            return this._messageContent;
        }
        set
        {
            this._messageContent = value;
            this.OnPropertyChanged("MessageContent");
        }
    }   
}

传出消息.cs

public class OutgoingMessage : Message
{
}

传入消息.cs

public class IncomingMessage : Message
{
}

这是如何工作 的,我覆盖了应用程序启动,以便我可以创建视图模型来填充我的 UI。您可以在 App.xaml.cs 代码中看到我创建窗口并显示它,然后添加消息。我打算使用计时器,但变得懒惰。

如果您查看 MainWindow.xaml,您会注意到我定义了 2 个 DataTemplates。其中一个以我的 IncomingMessageViewModel 为目标,另一个以 OutogingMessageViewModel 为目标。本地前缀是我的应用程序命名空间的别名。我有一个 ItemsControl 可以包含基本类型 Message 类,这样我就可以在同一个集合中同时拥有传入和传出消息。这绑定到我的 MainWindowViewModel 类的 Messages 属性。将传入和传出消息作为 2 个单独的类非常重要,因为这是使这项工作发挥作用的魔力。

另一种技术是使用属性和绑定到属性的样式选择器,正如其他答案之一所建议的那样,但这意味着我必须在我的 ViewModel 中处理 UI 特定逻辑(我不喜欢做)。

要更改任一消息类型的外观,只需更改相应 DataTemplate 中的 xaml 代码。

希望这可以帮助。

于 2013-03-14T08:14:54.290 回答
1

在 WPF 中,您需要添加

    <ListBox.ItemContainerStyle>
        <Style>
            <Setter Property="HorizontalAlignment" Value="{Binding WHATEVER}" />
        </Style>
    </ListBox.ItemContainerStyle>

到您的 ListBox 并将“WHATEVER”设置为具有指定对齐方式的项目的属性...我不知道这是否适用于 Windows Phone,但它似乎值得一试,因为您没有提到 ItemContainerStyle.. .

于 2013-03-14T03:31:05.493 回答
1

不要使用 Grid,而是使用带有 Horizo​​ntalAlignment="Stretch" 的 DockPanel。

至于数据对齐,假设您使用的是 ItemsSource,那么有一些解决方法。

首先,简单的做法是将 Horizo​​ntalAlignment WPF 属性添加到您的消息类中。消息类将确定 Horizo​​ntalAlignment 是左还是右。但是,这将使 UI 的依赖性更高。

代码将是这样的:

<TextBlock Text="{Binding Text}" HorizontalAlignment="{Binding MessageHAlign}" />

其次,更好(或干净)的方法是使用转换器(IValueConverter)进行水平对齐绑定。这更难,您必须定义自己的转换器,但您的代码会更整洁。然后您的消息有一个名为 MessageType 的收入或结果消息的枚举。然后在你的转换器中定义它:

public object Convert(object value, Type targetType, 
  object parameter, CultureInfo culture)
{
  if(parameter is MessageType){
    if(((MessageType)parameter) == MessageType.Income){
      return HorizontalAlignment.Left;
    }
    else{
      return HorizontalAlignment.Right;
    }
  }
}

以上代码未经测试,请慎重考虑。关于Converter的实现,请在一些地方搜索。如果没有帮助源,我仍然无法生成 Converter 绑定:)

于 2013-03-14T05:19:18.203 回答
0

试试这个并根据传入为左和传出为右设置属性

<DataTemplate>

<ListBoxItem>

 <Grid>
 <Grid Background="{Binding Color}" HorizontalAlignment="{Binding Property}">
  <TextBlock Text="{Binding Text}" FontSize="30"/>
 </Grid>
 </Grid>
 </ListBoxItem>
 </DataTemplate>
于 2013-03-14T04:43:23.720 回答
0

那将是我肮脏的工作示例

代码隐藏

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var l = new List<lItem>();

        for(int i=0;i<5;i++)
        {
        l.Add(new lItem(true,"aaa"+i));
        l.Add(new lItem(false,"bbb"+i));
        }

        sads.ItemsSource = l;

    }
}


public class lItem
{
    public string Text { get; set; }
    public Brush Color { get; set; }
    public HorizontalAlignment alig { get; set; }

    public lItem(bool ss, string str)
    {
        Text = str;
        Color = Brushes.Blue;
        alig = HorizontalAlignment.Right;

        if (ss)
        {
            Color = Brushes.Red;
            alig = HorizontalAlignment.Left;
        }
    }
}

Xaml

<ListBox Name="sads" Width="230">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <ListBoxItem>
                        <Grid Width="200">
                            <Label Background="{Binding Color}" VerticalAlignment="Top" HorizontalAlignment="{Binding alig}" >
                                <TextBlock Text="{Binding Text}" FontSize="30"/>
                            </Label>
                        </Grid>
                    </ListBoxItem>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

我建议使用触发器而不是在 ViewModel 中定义可视部分

于 2013-03-14T10:51:04.597 回答