這篇文章將講述實現WPF的UI和WCF中的雙工通信。實現文字部分的聊天功能和實現共享白板的功能。
畫WPF的界面其實是一件麻煩的事情。雖然WPF和WindowsForm一樣,能將控件拖到哪,它就在哪。我們在開發asp.net項目的時候用從原 始的table布局,到現在流行的div+css布局。這些都需要設計人員的仔細設計。這個程序的布局我采用Grid和StackPanel兩種方式。 Gird 類似html的表格布局,StackPanel就就像它的字面意思“堆棧面板”。
WPF的UI實現
首先新建一個wpf的應用程序,改名為ZqlChart。添加一個UserControl,用來實現登陸窗體,這個是用了StackPanel進行布局。XAML代 碼如下:
<UserControl x:Class="ZqlChart.LoginControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="210" Width="350" Loaded="UserControl_Loaded">
<StackPanel>
<Border Height="220" BorderBrush="#FFFFFFFF" BorderThickness="2,2,2,0" CornerRadius="5,5,0,0">
<Border.Background>
<LinearGradientBrush EndPoint="0.713,0.698" StartPoint="0.713,-0.139">
<GradientStop Color="#FFFFFFFF" Offset="0.933"/>
<GradientStop Color="LightBlue" Offset="0.337"/>
</LinearGradientBrush>
</Border.Background>
<StackPanel Name="infoPanel" Orientation="Vertical" Margin="10,10,10,10">
<StackPanel Name="typePanel" Orientation="Horizontal">
<RadioButton Name="chatTypeServer" FontSize="24" Margin="80,0,20,0"
Checked="chatTypeServer_Checked" VerticalContentAlignment="Center">服務端</RadioButton>
<RadioButton Name="chatTypeClient" FontSize="24" Checked="chatTypeClient_Checked" VerticalContentAlignment="Center">客戶端</RadioButton>
</StackPanel>
<StackPanel Name="serverPanel" Orientation="Horizontal" Margin="0,10,0,0">
<Label Name="lblServer" FontSize="20" Width="120" HorizontalContentAlignment="Right" VerticalContentAlignment="Center">服務端:</Label>
<TextBox Height="30" Name="txtServer" Width="160" FontSize="20" VerticalContentAlignment="Center" />
</StackPanel>
<StackPanel Name="usernamePanel" Orientation="Horizontal" Margin="0,10,0,10">
<Label Name="lblUserName" FontSize="20" Width="120" HorizontalContentAlignment="Right">用戶 名:</Label>
<TextBox Height="30" Name="txtUserName" Width="160" FontSize="20" VerticalContentAlignment="Center" />
</StackPanel>
<StackPanel Name="buttonPanel" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Name="btnLogin" Width="120" FontSize="20" Margin="10,10,10,10" Click="btnLogin_Click">連接 </Button>
<Button Name="btnCancel" Width="120" FontSize="20" Margin="10,10,10,10" Click="btnCancel_Click">取消 </Button>
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</UserControl>
界面效果如下:
聊天的主界面,如下圖:
大框架是3行3列。XAML代碼如下:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="ZqlChartMainWindow"
x:Class="ZqlChart.ZqlChartWindow"
Title="麒麟語音聊天室” Height="600" Width="800"
Background="#FF3B3737" Loaded="Window_Loaded" MinWidth="800" MinHeight="500">
<Grid x:Name="LayoutRoot" >
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="261" />
<RowDefinition Height="250" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="580*" />
<ColumnDefinition Width="48" />
</Grid.ColumnDefinitions>
<Border Name="BorderUsersList" Grid.Column="0" Grid.Row="1" Grid.RowSpan="2" CornerRadius="8,8,8,8" Background="LightBlue" BorderThickness="0,0,4,4" >
<ListView Name="lvUsers" Margin="10" FontSize="20">
<ListView.BitmapEffect>
<DropShadowBitmapEffect />
</ListView.BitmapEffect>
</ListView>
</Border>
<Border Name="BorderEditingType" Grid.ColumnSpan="3" CornerRadius="8,8,8,8" Background="LightBlue" BorderThickness="0,4,4,4">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Button Margin="0,0,0,0" Height="28" Width="121" Click="Button_Click" Background="White">
與其語音聊天
</Button>
<RadioButton Name="rbInk" Content="墨水" Margin="15,0,0,0" VerticalAlignment="Center" FontSize="20" IsChecked="True"
Tag="{x:Static InkCanvasEditingMode.Ink}" Click="rbInkType_Checked">
</RadioButton>
<RadioButton Name="rbEraserByStroke" Content="一筆一筆清除" Margin="15,0,0,0" VerticalAlignment="Center" FontSize="20"
Tag="{x:Static InkCanvasEditingMode.EraseByStroke}" Click="rbInkType_Checked">
</RadioButton>
<RadioButton Name="rbEraserByPoint" Content="一點一點清除" Margin="15,0,0,0" VerticalAlignment="Center" FontSize="20"
Tag="{x:Static InkCanvasEditingMode.EraseByPoint}" Click="rbInkType_Checked">
</RadioButton>
<TextBlock Margin="25,0,10,0" VerticalAlignment="Center" FontSize="20" >選擇顏色:</TextBlock>
<Button Margin="0,0,0,0" Background="White" Height="28" Width="64" Click="OnSetFill">
<Rectangle Width="54" Height="20" Stroke="Black" StrokeThickness="2">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding ElementName=ZqlChartMainWindow, Path=FillColor}" />
</Rectangle.Fill>
</Rectangle>
</Button>
</StackPanel>
</Border>
<Border Name="BorderInkCanvas" Grid.Column="1" Grid.Row="1" Background="LightBlue" BorderThickness="4,4,4,4" CornerRadius="8,8,8,8" Grid.ColumnSpan="2">
<InkCanvas x:Name="inkCanv" Margin="10" Background="White"
StrokeCollected="inkCanv_StrokeCollected" StrokeErasing="inkCanv_StrokeErasing"
StrokeErased="inkCanv_StrokeErased" VerticalAlignment="Top" >
</InkCanvas>
</Border>
<Border Name="BorderInkMessage" Grid.Column="1" Grid.Row="2" Background="LightBlue" BorderThickness="0,0,4,4" CornerRadius="8,8,8,8" Grid.ColumnSpan="2">
<Grid >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="500*" />
<ColumnDefinition Width="62*" />
<ColumnDefinition Width="62*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="1" Grid.Row="1" Background="LightBlue" BorderThickness="4,4,4,4" CornerRadius="8,8,8,8" >
<Button Content="發送" Height="23" Name="btnSend" Click="btnSend_Click" />
</Border>
<Border Grid.ColumnSpan="3" Background="LightBlue" BorderThickness="4,4,4,4" CornerRadius="8,8,8,8" >
<TextBox Name="txtAllMessage" >
<TextBox.BitmapEffect>
<DropShadowBitmapEffect />
</TextBox.BitmapEffect>
</TextBox>
</Border>
<Border Grid.Row="1" Background="LightBlue" BorderThickness="4,4,4,4" CornerRadius="8,8,8,8" >
<TextBox Grid.Row="1" Name="txtMessage" />
</Border>
<Border Grid.Column="2" Grid.Row="1" Background="LightBlue" BorderThickness="4,4,4,4" CornerRadius="8,8,8,8" >
<Button Content="關閉" Name="btnLeave" Height="23" FontSize="10" Click="btnLeave_Click">
</Button>
</Border>
</Grid>
</Border>
<Canvas Name="loginCanvas" Grid.Column="1" Grid.Row="1" Width="500" Height="300" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="39,78,41,0" Grid.RowSpan="2" />
</Grid>
</Window>
窗體就設計好了。
WCF雙工通信:
雙工通信能允許服務通知用戶當前的進度情況。我們可以通過使用指定CallbackContract的ServiceContract屬性的服務使用雙工,如 服務器端的代碼如下:
[ServiceContract(CallbackContract = typeof(IService1Callback))]
public interface IService1
{
[OperationContract]
string GetData(int value);
}
[ServiceContract]
public interface IService1Callback
{
[OperationContract]
void Reply(string message);
}
客戶端代碼:
class Program
{
static void Main(string[] args)
{
var callback = new Service1Callback();
var proxy = new Service1Client(new InstanceContext(callback));
Console.WriteLine(proxy.GetData(42));
Console.ReadLine();
}
}
class Service1Callback : IService1Callback
{
public void Reply(string message)
{
Console.WriteLine(message);
}
}
這篇文章中我將利用WCF的雙工通信實現文字聊天的功能和共享白板的功能。
定義協議:
public interface IZqlChartService
{
[OperationContract()]
bool Join(ChatUser chatUser);
[OperationContract()]
void Leave(ChatUser chatUser);
[OperationContract]
void SendBroadcastMessage(string strUserName, string message);
[OperationContract()]
bool IsUserNameTaken(string strUserName);
[OperationContract()]
void SendInkStrokes(MemoryStream memoryStream);
}
定義回調:
public interface IZqlChartServiceCallback
{
[OperationContract(IsOneWay = true)]
void NotifyMessage(string message);
[OperationContract(IsOneWay = true)]
void UpdateUsersList(List<ChatUser> listChatUsers);
[OperationContract(IsOneWay = true)]
void OnInkStrokesUpdate(ChatUser chatUser, byte[] bytesStroke);
[OperationContract(IsOneWay = true)]
void ServerDisconnected();
}
實現服務:
public class ZqlChartService : IZqlChartService
{
public static Dictionary<IZqlChartServiceCallback, ChatUser> s_dictCallbackToUser = new Dictionary<IZqlChartServiceCallback, ChatUser>();
public ZqlChartService()
{
}
public bool Join(ChatUser chatUser)
{
IZqlChartServiceCallback client = OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>();
if (s_dictCallbackToUser.ContainsValue(chatUser) == false)
{
s_dictCallbackToUser.Add(client, chatUser);
}
foreach (IZqlChartServiceCallback callbackClient in s_dictCallbackToUser.Keys)
{
callbackClient.UpdateUsersList(s_dictCallbackToUser.Values.ToList());
}
return true;
}
public void Leave(ChatUser chatUser)
{
IZqlChartServiceCallback client = OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>();
if (s_dictCallbackToUser.ContainsKey(client))
{
s_dictCallbackToUser.Remove(client);
}
foreach (IZqlChartServiceCallback callbackClient in s_dictCallbackToUser.Keys)
{
if (chatUser.IsServer)
{
if (callbackClient != client)
{
//server user logout, disconnect clients
callbackClient.ServerDisconnected();
}
}
else
{
//normal user logout
callbackClient.UpdateUsersList(s_dictCallbackToUser.Values.ToList());
}
}
if (chatUser.IsServer)
{
s_dictCallbackToUser.Clear();
}
}
public bool IsUserNameTaken(string strNickName)
{
foreach (ChatUser chatUser in s_dictCallbackToUser.Values)
{
if (chatUser.NickName.ToUpper().CompareTo(strNickName) == 0)
{
return true;
}
}
return false;
}
public void SendInkStrokes(MemoryStream memoryStream)
{
IZqlChartServiceCallback client = OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>();
foreach (IZqlChartServiceCallback callbackClient in s_dictCallbackToUser.Keys)
{
if (callbackClient != OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>())
{
callbackClient.OnInkStrokesUpdate(s_dictCallbackToUser[client], memoryStream.GetBuffer());
}
}
}
public void SendBroadcastMessage(string clientName, string message)
{
IZqlChartServiceCallback client =
OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>();
if (client != null)
{
foreach (IZqlChartServiceCallback callbackClient in s_dictCallbackToUser.Keys)
{
if (callbackClient != OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>())
{
callbackClient.NotifyMessage(clientName + ": " + message);
}
}
}
}
}
客戶端:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class ZqlChartServiceClient : System.ServiceModel.DuplexClientBase<IZqlChartService>, IZqlChartService
{
public ZqlChartServiceClient(System.ServiceModel.InstanceContext callbackInstance) :
base(callbackInstance)
{
}
public ZqlChartServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName) :
base(callbackInstance, endpointConfigurationName)
{
}
public ZqlChartServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress) :
base(callbackInstance, endpointConfigurationName, remoteAddress)
{
}
public ZqlChartServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(callbackInstance, endpointConfigurationName, remoteAddress)
{
}
public ZqlChartServiceClient(System.ServiceModel.InstanceContext callbackInstance, System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(callbackInstance, binding, remoteAddress)
{
}
public bool Join(ZqlChartObjects.ChatUser chatUser)
{
return base.Channel.Join(chatUser);
}
public void Leave(ZqlChartObjects.ChatUser chatUser)
{
base.Channel.Leave(chatUser);
}
public bool IsUserNameTaken(string strUserName)
{
return base.Channel.IsUserNameTaken(strUserName);
}
public void SendInkStrokes(System.IO.MemoryStream memoryStream)
{
base.Channel.SendInkStrokes(memoryStream);
}
public void SendBroadcastMessage(string strUserName, string message)
{
base.Channel.SendBroadcastMessage(strUserName, message);
}
}
客戶端回調類:
public class ClientCallBack : IZqlChartServiceCallback
{
public static ClientCallBack Instance;
private SynchronizationContext m_uiSyncContext = null;
private ZqlChartWindow m_mainWindow;
//ActiveCallWindow _activeCallForm;
//CallManager _callManager;
public ClientCallBack(SynchronizationContext uiSyncContext, ZqlChartWindow mainWindow)
{
m_uiSyncContext = uiSyncContext;
m_mainWindow = mainWindow;
}
public void OnInkStrokesUpdate(ZqlChartObjects.ChatUser chatUser, byte[] bytesStroke)
{
SendOrPostCallback callback =
delegate(object state)
{
m_mainWindow.OnInkStrokesUpdate(state as byte[] );
};
m_uiSyncContext.Post(callback, bytesStroke);
SendOrPostCallback callback2 =
delegate(object objchatUser)
{
m_mainWindow.LastUserDraw(objchatUser as ZqlChartObjects.ChatUser);
};
m_uiSyncContext.Post(callback2, chatUser);
}
public void UpdateUsersList(List<ZqlChartObjects.ChatUser> listChatUsers)
{
SendOrPostCallback callback =
delegate(object objListChatUsers)
{
m_mainWindow.UpdateUsersList(objListChatUsers as List<ZqlChartObjects.ChatUser>);
};
m_uiSyncContext.Post(callback, listChatUsers);
}
public void ServerDisconnected()
{
SendOrPostCallback callback =
delegate(object dummy)
{
m_mainWindow.ServerDisconnected();
};
m_uiSyncContext.Post(callback, null);
}
public void NotifyMessage(string message)
{
SendOrPostCallback callback =
delegate(object dummy)
{
m_mainWindow.NotifyMessage(message);
};
m_uiSyncContext.Post(callback, message);
}
public bool AcceptCall(string username)
{
//調獺?用?線?程ì必?須?為a STA,?因皚?為a許í多à UI 組哩?件t都?需è要癮。£
return MessageBox.Show(String.Format("Accep call from \"{0}\" ", username), "Incomming Call", MessageBoxButton.YesNo, MessageBoxImage.Information) == MessageBoxResult.Yes;
}
}
效果:
1、服務端登陸:
2、客戶端登錄:
3、文字聊天
4、共享白板:
總結:這篇文章實現了WPF的UI界面以及文字聊天和共享白板的功能。下一篇文章中將在此基礎上實現語音通話的功能。
出處:http://zhuqil.cnblogs.com